Personal Portfolio Website

A custom-built portfolio site showcasing DevSecOps projects with dark mode, responsive design, and iterative development philosophy

Frontend Development
Status: launched
Started:
Launched:

Tech Stack

AstroTypeScriptVercelMDXCSS GridGitHub
Version Historyv2.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

    flowchart TD
        A[Local Development] --> B[Git Commit]
        B --> C{Push to GitHub}
    
        C -->|main branch| D[Vercel Build]
        C -->|feature branch| G[Vercel Preview]
    
        D --> E[Production Deploy]
        E --> F[Live Site<br/>thehumble.dev]
    
        G --> H[Preview URL<br/>pr-*.vercel.app]
    
        I[Content Updates] -.-> A
        J[Component Changes] -.-> A
        K[Design Iterations] -.-> A
    
        style D fill:#4a6741,color:#fff,stroke:#3a5231,stroke-width:2px
        style E fill:#3a5231,color:#fff,stroke:#2a4221,stroke-width:2px
        style F fill:#2a4221,color:#fff,stroke:#1a3211,stroke-width:3px
        style G fill:#5a7751,color:#fff,stroke:#4a6741,stroke-width:2px
        style H fill:#6a8761,color:#fff,stroke:#5a7751,stroke-width:2px

    Key Components

    1. Astro Framework: Static site generation with component-based architecture for fast, SEO-friendly pages
    2. Content Collections: Structured Markdown/MDX for projects, blog posts, and wiki documentation with type-safe frontmatter
    3. Custom Components: Reusable .astro components for certifications, headers, footers, and navigation
    4. Dark Mode System: CSS custom properties with localStorage persistence and system preference detection
    5. Vercel Hosting: Continuous deployment from GitHub with automatic builds on push
    6. 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:

    1. Centralized all colors in CSS custom properties in global.css
    2. Created a systematic naming convention (--bg-primary, --bg-secondary, --text-primary)
    3. Audited every component and layout to replace direct color values with variables
    4. 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

    1. 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.

    2. Performance is a feature: Users notice fast sites. Astro’s zero-JS-by-default approach means the site loads instantly, even on slow connections.

    3. 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.

    4. 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.

    5. Content structure matters: Astro’s Content Collections with TypeScript schemas caught errors before deploy and made adding new projects trivial.

    v1.2 Update: Open Source Template Release

    What Changed

    Public Template Release: Extracted a sanitized, reusable version of this portfolio as an open-source template. The template strips all personal content and provides placeholder examples, making it easy for others to fork and customize.

    Repository Strategy:

    • Template Repository: github.com/hmbldv/portfolio-template - Public, scrubbed version for community use
    • Production Repository: Private, contains personal content and continuous deployment

    Template Features:

    • All personal content replaced with example placeholders
    • Comprehensive customization guide in README
    • Example project, blog post, and certification data
    • Ready-to-deploy Astro configuration

    Why Open Source?

    Building in public means sharing not just the finished product, but the tools that made it. This template represents hours of design decisions, responsive breakpoint tuning, and dark mode debugging—packaged so others can skip the boilerplate and focus on their content.

    The separation between template and production repos also demonstrates proper handling of public vs. private code—a relevant skill for DevSecOps work where secrets management is critical.

    v2.0 Update: Project Versioning System

    The Problem

    As my portfolio grew, I realized some projects evolve significantly over time. The AWS Security Infrastructure started as a simple Terraform state backend and grew into a comprehensive compliance platform. But the original writeup was lost—replaced by the updated version with no way to see the journey.

    This raised a content strategy question: How do you document project evolution without cluttering the portfolio?

    The Solution

    Built a complete project versioning system inspired by documentation sites like MDN and Astro’s own docs. The system supports:

    1. Previous Version Archives

    • Projects can have archived versions (v1-0.md, v2-0.md)
    • Each version is a complete snapshot of the writeup at that point
    • Previous versions are read-only historical records

    2. Version History Dropdown

    • Shows current version with badge
    • Lists all previous versions with dates and summaries
    • On previous version pages, shows “View Current Version” link
    • Consistent design language across current and archived pages

    3. Changelog Section

    • Tracks all updates (major and minor)
    • Shows version, date, and summary for each change
    • Appears at the bottom of project pages

    4. Documentation-Style Layout

    • Two-column grid on desktop (sticky TOC sidebar + content)
    • Mobile-responsive with collapsible TOC
    • Breadcrumb navigation for context
    • Visual hierarchy with H1 dominant, TOC secondary

    Technical Implementation

    Schema Updates (Zod validation):

    version: z.string().optional(),
    versionStatus: z.enum(['current', 'previous']).optional(),
    majorVersions: z.array(z.object({
      version: z.string(),
      date: z.coerce.date(),
      summary: z.string(),
      slug: z.string(),
    })).optional(),
    changelog: z.array(z.object({
      version: z.string(),
      date: z.coerce.date(),
      summary: z.string(),
    })).optional(),

    Folder Structure (nested for versioning):

    src/content/projects/
    ├── portfolio-website/
    │   ├── index.md      ← Current version (v2.0)
    │   └── v1-0.md       ← Archived version (v1.0)
    ├── aws-security-infrastructure/
    │   ├── index.md      ← Current version (v2.0)
    │   └── v1-0.md       ← Archived version (v1.0)
    └── dar-dmana-catering/
        └── index.md      ← No versioning needed (v1.0)

    Dynamic Routing:

    • /projects/portfolio-website/ → Current version
    • /projects/portfolio-website/v1-0/ → Previous version
    • Astro’s catch-all [...slug].astro handles both patterns

    Content Strategy Decision

    During implementation, I realized not every project needs versioning. The v2.0 updates for four projects (Dar Dmana, AWS Resource Discovery, Security Lab, Claude Code Automation) were just “documentation improvements”—the actual project content hadn’t changed.

    Versioning criteria established:

    • ✅ Version when the project itself significantly evolved
    • ✅ Version when the writeup documents genuinely different functionality
    • ❌ Don’t version for documentation formatting updates
    • ❌ Don’t version for minor content improvements

    This led to cleaning up unnecessary versions, keeping versioning only for:

    • portfolio-website: v1.0 (initial launch) → v2.0 (versioning system + layout refactor)
    • aws-security-infrastructure: v1.0 (state backend foundation) → v2.0 (compliance platform)

    New Components Created

    1. VersionHistory.astro: Dropdown showing version history or “viewing previous version” notice
    2. Changelog.astro: Renders version history timeline
    3. Breadcrumbs.astro: Navigation context (Home → Projects → Project Name)
    4. PreviousVersionBanner.astro: Alert styling for archived pages (integrated into VersionHistory)

    Design Decisions

    Why sticky TOC sidebar? Long project writeups (like this one) benefit from persistent navigation. The sticky sidebar keeps the table of contents visible while scrolling, improving discoverability of sections.

    Why integrate version notice into dropdown? Originally had a separate banner for previous versions. But this created visual inconsistency—current pages had a dropdown, previous pages had a banner. Integrating them means identical design language regardless of which version you’re viewing.

    Why use dashes in filenames (v1-0.md)? Astro’s glob loader treats dots as file extensions. v1.0.md becomes slug v10, breaking URLs. Using v1-0.md preserves the intended slug v1-0.

    Future Enhancements

    Completed in v1.1

    • Table of contents on long-form content
    • Improved mobile navigation (hamburger menu)
    • Button styling consistency

    Completed in v1.2

    • Open source template release
    • Repository separation (template vs. production)
    • Comprehensive customization documentation

    Completed in v2.0

    • Project versioning system with archive support
    • Documentation-style two-column layout
    • Sticky TOC sidebar on desktop
    • Version history dropdown component
    • Breadcrumb navigation
    • Changelog component

    Short-term (v2.1)

    • Resume transcription: Make resume content searchable and linkable
    • Photo integration: Add professional headshot and project screenshots
    • Analytics: Privacy-respecting analytics (Plausible or GoatCounter)

    Medium-term (v3.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 (v4.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


    This project demonstrates proficiency in frontend development, responsive design, performance optimization, open-source contribution, and pragmatic engineering. It showcases the ability to build clean, fast, user-focused applications while maintaining code quality and iterative development practices.

    Commit History

    3a66643hmbldv/portfolio

    refactor(projects): sort by update date and hide card tags

    Sort project cards by most recently updated. Hide tech stack tags on listing for cleaner cards.

    6a638e3hmbldv/portfolio

    feat(changelog): convert to commit-based changelog with GitHub links

    Redesign the changelog system to display actual git commits instead of manual version entries. Schema changes include commit hash, message, body, url, and repo fields. Component renamed to Commit History with GitHub link badges.