About This Site

A portfolio that's also a portfolio piece.

Architecture

This site is a full-stack web application built from scratch — no CMS, no framework boilerplate. Every page is server-rendered by Flask using Jinja2 templates, with a MySQL database driving the resume content. That means the resume page is genuinely data-driven: add a bullet to the database and it shows up on the page immediately, correctly grouped and filtered.

Browser → nginx (reverse proxy) → gunicorn (WSGI) → Flask app │ Jinja2 templates │ SQLAlchemy ORM → MySQL DB

Deploying on a Linux VPS with nginx as a reverse proxy means the static files are served efficiently without hitting Python at all. gunicorn manages the worker processes, keeping the app responsive under load.

Tech Stack

Flask

Python micro-framework for routing, view logic, and Jinja2 template rendering. Lightweight and explicit — nothing hidden.

SQLAlchemy

ORM for database queries. Models defined in Python, queries composed with the fluent API — no raw SQL in views.

MySQL

Relational database storing companies, positions, and resume bullets with JSON columns for tools, skills, and impact data.

Jinja2

Server-side templating with template inheritance. base.html defines layout; child templates fill named blocks.

Vanilla JS

No frameworks. The resume filter, carousel, and skill icon swap are all plain ES6 — fast, dependency-free, easy to audit.

CSS Custom Properties

Design system built on :root variables — color palette, spacing scale, type scale — making visual changes one-line operations.

nginx + gunicorn

Production deployment: nginx handles TLS and static files; gunicorn manages Python worker processes.

python-dotenv

Secrets and environment-specific config live in .env, never in source code. Credentials are injected at runtime.

Design Decisions

  • Server-side rendering over a SPA: Flask + Jinja2 means every page is a fast, indexable HTML document. No JavaScript required to see any content.
  • Data-driven resume: Resume bullets are rows in a database, not hardcoded HTML. This enables filtering by skill category, future versioning, and easy updates without touching templates.
  • JSON columns for flexible metadata: Skills, tools, and impact data are stored as JSON — structured enough to query, flexible enough to evolve without schema migrations.
  • No build step: CSS and JS are written directly — no webpack, no transpilation. Keeps the feedback loop fast and the toolchain simple.
  • CSS Custom Properties as a design system: All colors, spacing, and typography are defined as variables at :root. Theming a page means changing one variable, not hunting through hundreds of rules.