Interactive Resume Website

A comprehensive look at building a modern, responsive personal portfolio website from scratch, featuring real-time analytics, dynamic user interactions, and a full-stack architecture using Cloudflare Workers and D1 database.

View Live Site → View Source Code
Desktop Site
HTML5 CSS3 Vanilla JavaScript Cloudflare Workers D1 Database GitHub Pages

Project Overview

I set out to create a resume that went beyond a static PDF, something more dynamic, interactive, and personalized. The goal was to design a responsive website that could showcase not only my experience and skills but also my growth in web development and backend integration. It's aimed at recruiters, instructors, and anyone curious about my work. This was a solo project with the scope of creating a virtual resume that could later be connected to our project portfolio.            

I wanted to create something that would engage visitors and provide insights into how people interact with my content. This led me to implement features like real-time analytics, interactive project previews, and a contact system that notifies me instantly when someone reaches out.

The project represents a full-stack approach to web development, combining frontend design skills with backend data management and third-party integrations. Every feature was built with both user experience and technical learning in mind.

Technical Implementation

This project uses a lightweight tech stack: just HTML, CSS, and JavaScript on the frontend. The website is hosted on GitHub Pages, which provides free static hosting with excellent performance. However, for the backend functionality, I needed something more powerful.

For the backend, I used Cloudflare Workers paired with Cloudflare D1, a serverless SQL database powered by SQLite. To connect GitHub Pages with Cloudflare's services, I updated my domain's nameservers to point to Cloudflare.

Why Cloudflare for Backend Services?
Using Cloudflare's edge computing platform gives me the benefits of their global CDN (faster loading times worldwide), advanced security features, and access to serverless computing—all while keeping my static site on GitHub Pages. This hybrid approach combines the simplicity of static hosting with the power of serverless functions.

Development Environment Setup

Setting this up was a big learning experience. Since D1 runs through Cloudflare's ecosystem, I needed to install several tools:

  • Node.js – the JavaScript runtime environment
  • npm – its package manager, used to install the needed tooling
  • Wrangler – Cloudflare's command-line interface tool that helps deploy and manage projects
  • SSH – to handle secure deployment to Cloudflare's infrastructure

After configuring the D1 database locally and defining the schema, I deployed everything via the command line using wrangler d1 and wrangler publish. This setup allowed me to handle form submissions, store analytics events, and track user sessions without relying on traditional hosting with a database server.

Key Features & Implementation

Some of the standout features that make this more than just a static website include:

  • Mobile-first responsive design so it looks great on any screen size
  • Theme toggle with a moon/sun button to switch between light and dark modes
  • Real-time analytics showing how people interact with my resume
  • Downloadable PDF resume for traditional applications
  • Interactive project previews that appear when you hover over project cards
  • Contact form integration via Telegram with secure backend processing

The Theme Toggle System

One feature I'm particularly proud of is the theme switching capability. It uses CSS custom properties (variables) to seamlessly transition between light and dark modes, and remembers your preference for future visits using browser storage.

Theme Toggle Implementation JavaScript

function toggleTheme() { 

  const html = document.documentElement

  const currentTheme = html.getAttribute('data-theme'); 

  const newTheme = currentTheme === 'dark' ? null : 'dark'

 

  if (newTheme) { 

   html.setAttribute('data-theme', newTheme); 

   localStorage.setItem('theme', 'dark'); 

   document.getElementById('theme-icon').textContent = '☀️'

 } else

  html.removeAttribute('data-theme'); 

  localStorage.setItem('theme', 'light'); 

  document.getElementById('theme-icon').textContent = '🌙'

 } 

 

  // Track the theme change for analytics 

  trackEvent('theme_toggle', { 

   theme: newTheme || 'light' 

  });

 

// Load saved theme on page initialization 

function loadTheme() { 

 const savedTheme = localStorage.getItem('theme') || 'light'

 const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches

 const theme = savedTheme === 'auto' ? (prefersDark ? 'dark' : 'light') : savedTheme

 

 if (theme === 'dark') { 

  document.documentElement.setAttribute('data-theme', 'dark'); 

  document.getElementById('theme-icon').textContent = '☀️'

 } else

   document.getElementById('theme-icon').textContent = '🌙'

 } 

}

Challenges & Solutions

1. Working with Cloudflare D1 for the First Time

This was my first time deploying a live database using serverless SQL. Unlike traditional database setups where you might use phpMyAdmin or similar tools, everything with D1 is defined and pushed through Wrangler command-line tools. Here's a glimpse of the schema I created to track user sessions:

Database Schema for User Sessions SQL

CREATE TABLE IF NOT EXISTS sessions (
  session_id TEXT PRIMARY KEY

  total_time INTEGER

  active_pages INTEGER

  avg_time_per_page INTEGER

  total_events INTEGER

  is_active INTEGER

  user_agent TEXT

  referrer TEXT

  screen_resolution TEXT

  timestamp TEXT ); 

CREATE TABLE IF NOT EXISTS events

  id INTEGER PRIMARY KEY AUTOINCREMENT

  session_id TEXT

  event_name TEXT

  event_data TEXT

  timestamp TEXT );

Getting this to work involved debugging the deployment steps and making sure timestamps were recorded consistently. The serverless nature of D1 meant I had to think differently about database connections and query optimization.

2. Getting Comfortable with Adobe Dreamweaver

Although many developers today use code editors like VS Code, I decided to try Adobe Dreamweaver on my own to see how it could help with design visualization. It came with a learning curve—especially around how it handles asset paths and previews—but it helped me better understand front-end layout behaviors in a visual way.

The split view feature was particularly useful for seeing how CSS changes affected the layout in real-time, which helped me catch responsive design issues earlier in the development process.

3. Reliable User Activity Tracking

One of the more technical hurdles was capturing user sessions accurately. A lot can go wrong: users close tabs, leave pages open, or go idle. To handle this, I wrote logic that tracks when the page is hidden, when the user is idle for too long, or when they navigate away:

Session Tracking Logic JavaScript

// Multiple approaches to catch user departure 

document.addEventListener('visibilitychange', () => { 

  if (document.hidden) { 

   trackEvent('page_blur'); 

   sendAnalytics(true); // Send immediately when page becomes hidden 

  } else

    trackEvent('page_focus'); 

  } 

}); 

 

// Reliable for navigation away from page 

window.addEventListener('pagehide', () => { 

 sendAnalytics(true); 

}, { passive: true }); 

 

// Handle idle users 

let idleTimer

 

const resetIdleTimer = () => { 

 clearTimeout(idleTimer); 

 idleTimer = setTimeout(() => { 

   sendAnalytics(); // Send after 2 minutes of inactivity

 }, 120000); 

}; 

 

// Reset timer on user activity 

[

 'mousedown'

 'mousemove'

 'keypress'

 'scroll'

 'touchstart'

].forEach(event => { 

 document.addEventListener(event, resetIdleTimer, { passive: true }); 

}); 

 

resetIdleTimer(); // Start the timer

This multi-layered approach helped improve session accuracy and allowed me to calculate meaningful metrics like average time per page and user engagement patterns.

Visual Showcase

Screenshots of the desktop and mobile views, dark/light mode toggles, and a peek at the analytics dashboard really help bring the work to life. One of my favorite details is how the navigation transitions are smooth and snappy, making the site feel more like an app than a static page.

Light mode view of Education tab on Interactive Resume
Light mode view of website on mobile
Dark mode view of Education tab on Interactive Resume
Dark mode view of website on mobile
Analytics Dashboard with visitor stats
Analytics Dashboard

Performance & Analytics

I implemented a lightweight system to collect metrics like:

  • How long visitors stay on each section
  • Which pages they view and in what order
  • Whether they download my resume or toggle themes
  • What device and browser they're using
  • Which projects they click on or interact with

All of this is viewable through a custom analytics dashboard I built myself. It even shows what device the user was on and tracks specific interactions like project clicks:

Analytics Event Example JSON

  "event": "project_click"

  "timestamp": 1702345678901

  "session_id": "abc123def456"

  "data": { 

    "project": "Interactive Resume"

    "section": "home"

    "user_agent": "Chrome/Mobile"

    "screen_resolution": "375x667" 

  } 

}

Analytics Dashboard Features
The custom dashboard shows real-time visitor data, session tracking, device analytics, and user interaction patterns. It includes features like active session monitoring, popular content identification, and visitor flow analysis—all built without relying on third-party analytics services.

The analytics dashboard provides insights that help me understand which content resonates most with visitors and how they navigate through the site. This data-driven approach helps me make informed decisions about future improvements.

Notable Code Implementations

Contact Form Integration with Telegram

One of the most practical features is the contact form that sends messages directly to my Telegram. I chose to route this through Cloudflare Workers for security reasons—since GitHub Pages repositories are public, anyone could view my source code and potentially access sensitive API tokens if they were stored in the frontend.

Security Through Backend Processing
By processing form submissions through Cloudflare Workers, sensitive credentials like Telegram bot tokens and chat IDs remain secure in environment variables, never exposed in the public GitHub repository. This approach also allows for input validation and spam protection on the server side.
Telegram Contact Worker JavaScript (Cloudflare Worker)

export default {

  async fetch(request, env, ctx) {

    // Handle CORS preflight requests

    if (request.method === "OPTIONS") {

      return new Response(null, { status: 204, headers: corsHeaders() });

    }

    // Only allow POST requests

    if (request.method !== "POST") {

      return new Response("Method not allowed", { status: 405, headers: corsHeaders() });

    }

    try {

      const data = await request.json();

      const { name, email, subject, message } = data;

      // Format timestamp in Eastern Time

      const easternFormatter = new Intl.DateTimeFormat("en-US", {

        timeZone: "America/New_York",

        weekday: "short", year: "numeric", month: "short",

        day: "numeric", hour: "numeric", minute: "2-digit", hour12: true

      });

      const formattedTime = easternFormatter.format(new Date());

      // Construct message for Telegram

      const text = `🔔 New Contact Form Submission\n\n` +

        `👤 Name: ${name}\n` +

        `📧 Email: ${email}\n` +

        `📋 Subject: ${subject}\n` +

        `💬 Message: ${message}\n\n` +

        `🕒 Time (ET): ${formattedTime}`;

      const telegramUrl = `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`;

      const res = await fetch(telegramUrl, {

        method: "POST",

        headers: { "Content-Type": "application/json" },

        body: JSON.stringify({

          chat_id: env.TELEGRAM_CHAT_ID,

          text: text,

          parse_mode: "Markdown"

        })

      });

      return new Response(res.ok ? "Success" : "Failed", {

        status: res.ok ? 200 : 500,

        headers: corsHeaders()

      });

    } catch (error) {

      return new Response("Server error", { status: 500, headers: corsHeaders() });

    }

  }

};

 

// Helper function for CORS headers

function corsHeaders() {

  return {

    "Access-Control-Allow-Origin": "*",

    "Access-Control-Allow-Methods": "POST, OPTIONS",

    "Access-Control-Allow-Headers": "Content-Type"

  };

}

This lets me get real-time form alerts directly to my phone, while keeping all sensitive credentials secure and hidden from public view.

Lessons Learned

This project taught me valuable lessons that extend far beyond just writing code:

Technical Growth

I became more comfortable with backend concepts like RESTful APIs, SQL schemas, and data validation. Working with serverless architecture showed me how modern web applications can scale efficiently without managing traditional servers.

Learning to handle cross-origin requests, timezone formatting, and real-time data transmission gave me a deeper understanding of how web applications communicate across different systems and time zones.

User Experience Insights

The analytics data revealed surprising patterns about how people actually use the site. For example, I discovered that mobile users spend significantly more time reading the technical skills section, which influenced me to optimize that section's mobile layout.

I also learned the importance of user experience design, especially on mobile devices. Small details like touch target sizes, loading animations, and smooth transitions make a significant difference in how professional the site feels.

Development Workflow

Managing deployment and live debugging through command-line tools was initially challenging but ultimately taught me valuable DevOps skills. Understanding how to troubleshoot issues in a production environment, read server logs, and deploy updates safely are crucial skills for any web developer.

"Building this project taught me that great web development isn't just about writing code—it's about understanding users, measuring outcomes, and continuously improving based on real data."

Future Improvements

Here are some enhancements I plan to make:

Enhanced Security & Reliability

  • Bot protection for the contact form—potentially using a honeypot field or CAPTCHA-like behavior to prevent spam
  • Rate limiting on form submissions to prevent abuse
  • Input sanitization improvements for better security

User Experience Enhancements

  • Multilingual support (i18n) to make the site accessible to a wider audience
  • Heatmap or scroll-depth analytics to see which sections people engage with most
  • Progressive Web App features like offline functionality and installability
  • Enhanced accessibility with better screen reader support and keyboard navigation

Development Process

  • CI/CD automation so future updates are deployed with less manual effort
  • Automated testing to catch issues before they reach production
  • Performance monitoring to track loading times and user experience metrics

The modular architecture I've built makes these enhancements straightforward to implement without disrupting the existing functionality that's already working well.