Personal Portfolio Website
A custom-built portfolio site showcasing DevSecOps projects with dark mode, responsive design, and iterative development philosophy
Tech Stack
Viewing Previous Versionv1.0
Table of Contents
Problem Statement
Building a portfolio that actually represents who you are is harder than it sounds. Most portfolio sites fall into one of two traps: they’re either cookie-cutter templates that look like everyone else’s, or they’re over-engineered showcases that prioritize flash over substance.
I wanted something different. A site that stood out not because it tried to impress, but because it incorporated the design elements I personally care about: clean organization, quick performance, and an iterative development approach that values shipping real work over endless polishing. This needed to be a true reflection of how I think about building systems—pragmatic, security-conscious, and focused on delivering value.
The challenge wasn’t just technical. It was philosophical: How do you create something authentic in a space dominated by marketing-heavy portfolios? How do you showcase DevSecOps work—which is often behind-the-scenes infrastructure—in a way that’s engaging and representative of actual accomplishments?
Solution Architecture
I built a custom portfolio from scratch using Astro, a modern static site generator that prioritizes performance and developer experience. The architecture is intentionally simple: a file-based routing system with Markdown content collections for blog posts, projects, and wiki pages.
Deployment Pipeline
graph LR
A[Local Development] --> B[Git Commit]
B --> C[GitHub Push]
C --> D[Vercel Auto-Deploy]
D --> E[Live Site]
F[Content Changes] --> A
G[Component Updates] --> A
H[Design Iterations] --> A
style D fill:#4a6741,color:#fff
style E fill:#3a5231,color:#fff
Key Components
- Astro Framework: Static site generation with component-based architecture for fast, SEO-friendly pages
- Content Collections: Structured Markdown/MDX for projects, blog posts, and wiki documentation with type-safe frontmatter
- Custom Components: Reusable
.astrocomponents for certifications, headers, footers, and navigation - Dark Mode System: CSS custom properties with localStorage persistence and system preference detection
- Vercel Hosting: Continuous deployment from GitHub with automatic builds on push
- Responsive Design: Mobile-first CSS Grid layouts with breakpoints at 768px (tablet) and 1024px (desktop)
Technical Implementation
Framework Selection: Astro
I chose Astro over Next.js, Gatsby, or traditional SSGs because:
- Zero JavaScript by default: Ships only HTML/CSS unless you opt into client-side JS
- Content Collections: First-class support for structured Markdown with TypeScript validation
- Component Islands: Selective hydration for interactive elements (like the dark mode toggle)
- Performance: 100/100 Lighthouse scores out of the box
- Flexibility: Supports multiple UI frameworks (React, Vue, Svelte) if needed later
Site Architecture
The site is organized into four main sections:
1. Projects (/projects): Showcases DevSecOps and cloud security work with custom category filtering (AWS Security, AWS Infrastructure, Homelab, AI Tools, Local Development)
2. Blog (/blog): Technical writeups and learning notes with RSS feed generation and automatic table of contents
3. Wiki (/wiki): Knowledge base and documentation organized by topic with search-friendly structure
4. About (/about): Professional background with custom certification badge component linked to Credly for verification
Dark Mode Implementation
The dark mode system was one of the most critical features. It needed to:
- Persist user preference across sessions
- Respect system dark mode settings on first visit
- Prevent flash of unstyled content (FOUC)
- Support all components and layouts
Implementation approach:
// Inline script in <head> prevents FOUC
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
if (theme === 'dark') {
document.documentElement.classList.add('dark');
}
CSS Custom Properties in global.css:
:root {
--bg-primary: #ffffff;
--bg-secondary: #f7fafc;
--text-primary: rgb(var(--gray-dark));
/* Light mode colors */
}
:root.dark {
--bg-primary: #0f1410; /* Black with green tint */
--bg-secondary: #1a211a; /* Dark grey-green */
--text-primary: rgb(var(--gray-dark));
/* Dark mode: black/grey/forest green palette */
}
The dark mode toggle component uses SVG icons (sun/moon) with smooth transitions and proper ARIA labels for accessibility.
Custom Certification Badges
Rather than embedding Credly’s iframe widgets (which are slow and break responsive layouts), I built a custom CertificationBadge.astro component:
interface Props {
name: string;
issuer: string;
credlyUrl: string;
dateIssued: string;
expirationDate?: string;
inProgress?: boolean;
badgeImageUrl?: string;
}
Features:
- Displays completed certifications with issued/expiration dates
- Shows “In Progress” status for certifications being pursued
- Links to Credly for verification
- Responsive grid layout (3 columns desktop, 2 tablet, 1 mobile)
- Pulls badge images directly from Credly CDN for fast loading
Data structure in src/data/certifications.ts keeps content separate from presentation, making updates simple.
Responsive Design Strategy
Mobile-first CSS with three breakpoints:
- Mobile: 320px - 767px (1-column layouts, simplified navigation)
- Tablet: 768px - 1023px (2-column grids, collapsible menus)
- Desktop: 1024px+ (3-column grids, sticky sidebars, full navigation)
Used CSS Grid for all layouts instead of flexbox for better control over two-dimensional spacing. Implemented CSS custom properties for fluid spacing using clamp():
--space-md: clamp(1rem, 4vw, 1.2rem);
--space-lg: clamp(1.5rem, 5vw, 2rem);
This creates responsive spacing that adapts smoothly across all screen sizes without breakpoint jumps.
Challenges & Solutions
Challenge 1: Avoiding Template Paralysis
Problem: Most portfolio templates are either too opinionated (forcing a specific structure) or too basic (requiring extensive customization). Starting from a template meant inheriting someone else’s design decisions.
Solution: Started completely from scratch with Astro’s minimal blog template and stripped it down to barebones. Built only what was needed, iteratively adding features based on real content requirements.
Lesson Learned: Starting simple and building up is faster than starting complex and stripping down. The initial discomfort of a blank slate forces intentional design decisions.
Challenge 2: Dark Mode Consistency
Problem: Implementing dark mode across all components and layouts revealed inconsistencies. Some pages used direct color values, others used CSS variables, and cards in dark mode had poor contrast.
Solution:
- Centralized all colors in CSS custom properties in
global.css - Created a systematic naming convention (
--bg-primary,--bg-secondary,--text-primary) - Audited every component and layout to replace direct color values with variables
- Tested both modes on every page to ensure contrast ratios met WCAG AA standards
Lesson Learned: Theme consistency requires discipline. A centralized design system (even a simple one) prevents drift and makes changes predictable.
Challenge 3: Content Organization
Problem: Portfolio content spans multiple formats: projects (technical writeups), blog posts (learning notes), wiki pages (reference docs), and certifications. Each needed different presentation but consistent navigation.
Solution: Used Astro Content Collections with custom frontmatter schemas for each type:
- Projects: category, status, techStack, repoUrl, liveUrl
- Blog: pubDate, updatedDate, heroImage, tags
- Wiki: category, order, relatedLinks
This created type-safe content validation and enabled powerful filtering/sorting without a CMS.
Lesson Learned: Structure your content based on how you’ll query it, not just how you’ll display it. Good information architecture makes features easier to build later.
Challenge 4: Deployment Simplicity
Problem: Wanted continuous deployment without managing infrastructure or complex CI/CD pipelines.
Solution: Connected GitHub repo to Vercel with automatic deployments on push to main. Zero configuration needed—Vercel detected the Astro framework and configured build settings automatically.
Lesson Learned: For static sites, serverless platforms like Vercel eliminate operational overhead. The best deployment pipeline is the one you don’t have to think about.
Results & Metrics
Performance
- Lighthouse Score: 100/100 (Performance, Accessibility, Best Practices, SEO)
- First Contentful Paint: < 0.5s
- Time to Interactive: < 1.0s
- Total Bundle Size: ~50KB (gzipped)
Development Velocity
- Initial launch: 2 days (from empty repo to deployed site with content)
- Dark mode implementation: 4 hours (including fixing consistency issues across all pages)
- Custom certification system: 2 hours (component + data structure + integration)
Design Achievements
- ✅ Dark mode with localStorage persistence and system preference detection
- ✅ Custom certification badges replacing slow iframe embeds
- ✅ Responsive design tested on mobile, tablet, and desktop
- ✅ Accessible navigation with ARIA labels and keyboard support
- ✅ Auto-generated table of contents on project and blog pages
- ✅ RSS feed for blog subscribers
Code Quality
- TypeScript for type safety across components and content
- Conventional commits for clear git history
- Component-based architecture for reusability
- No external UI libraries (pure CSS for styling)
Key Takeaways
-
Iterative beats perfect: Shipped a working site in 2 days, then improved based on real use. Waiting to launch until everything is “perfect” means never launching.
-
Performance is a feature: Users notice fast sites. Astro’s zero-JS-by-default approach means the site loads instantly, even on slow connections.
-
Authenticity over marketing: The site represents actual work—projects I built, certifications I earned, writing that reflects how I think. No stock photos, no generic copy, no inflated claims.
-
Design systems start small: CSS custom properties for colors and spacing provided 90% of the benefit of a full design system with 10% of the complexity.
-
Content structure matters: Astro’s Content Collections with TypeScript schemas caught errors before deploy and made adding new projects trivial.
Future Enhancements
Short-term (v1.1)
- Resume transcription: Make resume content searchable and linkable
- Photo integration: Add professional headshot and project screenshots
- Analytics: Privacy-respecting analytics (Plausible or GoatCounter) to understand which content resonates
Medium-term (v2.0)
- Search functionality: Client-side search across projects, blog, and wiki using Fuse.js
- Project filtering: Interactive category/tag filters on projects page
- Reading time estimates: Automatic calculation for blog posts
- Related content: Show related projects/posts based on tags
Long-term (v3.0+)
- Automated content generation: LLM-assisted draft creation for project writeups from git commit history
- Interactive demos: Embed sandboxes or live demos for frontend projects
Resources
- Live Site: www.thehumble.dev
- Repository: github.com/hmbldv/portfolio
- Framework: Astro Documentation
- Hosting: Vercel Platform
This project demonstrates proficiency in frontend development, responsive design, performance optimization, and pragmatic engineering. It showcases the ability to build clean, fast, user-focused applications while maintaining code quality and iterative development practices.