This file is the canonical reference for AI coding agents working on this repository. The project is a static personal developer portfolio and open-source showcase for Ali Tavallaie, deployed at https://techbend.dev.
This repository produces a Hugo-based static site that serves as:
- A personal portfolio and developer landing page
- A live dashboard of open-source Python packages published to PyPI
- A showcase of technical books and writing
- A contact and booking hub (via Cal.com)
The site is split into two distinct systems:
- Hugo Static Site (
content/,themes/,layouts/,public/) - Python Data Pipeline (
scripts/,static/data/) — syncs PyPI download statistics from BigQuery and ClickPy, plus GitHub profile data
There is no backend server at runtime; everything is either pre-built static HTML or client-side JavaScript fetching public APIs.
| Layer | Technology | Version / Notes |
|---|---|---|
| Static Site Generator | Hugo (Extended) | 0.125.0 (pinned in CI) |
| CSS Architecture | Custom CSS | Modular files: reset, tokens, base, layout, components, effects |
| CSS Features | Modern CSS | @layer, @property, CSS variables, backdrop-filter, container queries |
| Frontend JS | Vanilla JavaScript | No frameworks, no build step beyond minification |
| Python | CPython | >=3.12 (specified in pyproject.toml and CI) |
| Python Package Manager | uv |
uv.lock present; virtualenv at .venv/ |
| Data Source | BigQuery Public Data | bigquery-public-data.pypi.file_downloads |
| Data Source | ClickPy (ClickHouse) | sql-clickhouse.clickhouse.com — public PyPI mirror |
| Hosting | GitHub Pages | Deployed via GitHub Actions |
Notably absent: Node.js, npm, Docker, test frameworks, CSS frameworks (Tailwind, Bootstrap, DaisyUI), or JS frameworks (React/Vue).
Note on CSS Architecture: The theme uses a fully custom CSS design system with no external CSS frameworks. Styles are split into logical modules (reset, tokens, base, layout, components, effects) and concatenated via Hugo's
resources.Concat. This provides complete creative control while keeping the build dependency-free.
├── archetypes/ # Hugo archetypes (empty)
├── assets/ # Project-level assets (empty; theme owns all assets)
├── content/ # Markdown content pages
│ ├── about.md
│ ├── books/
│ │ └── _index.md
│ ├── projects.md
│ └── resume.md
├── static/data/ # Generated data files
│ ├── github.json # GitHub profile, pinned repos, contributions
│ ├── manifest.json # Combined manifest (GitHub + PyPI top packages)
│ ├── pypi.json # PyPI package manifest
│ └── pypi/ # Per-package JSON + CSV stats
├── env/ # GCP service account key (local development only)
├── layouts/ # Project-level Hugo layouts (empty; theme provides all)
├── public/ # Hugo build output (generated; do not commit)
├── resources/ # Hugo resource cache
├── scripts/ # Python automation
│ ├── diagnose_clickpy.py
│ ├── discover_packages.py
│ ├── generate_manifest.py
│ ├── sync_github.py
│ ├── sync_stats_bigquery.py
│ └── sync_stats_clickpy.py
├── static/ # Static files served directly (generated data only)
├── themes/techbend/ # Custom Hugo theme (fully custom CSS)
│ ├── assets/
│ │ ├── css/
│ │ │ ├── reset.css # Modern CSS reset
│ │ │ ├── tokens.css # Design tokens (colors, spacing, typography)
│ │ │ ├── base.css # Base styles, typography, selection
│ │ │ ├── layout.css # Containers, grids, utilities
│ │ │ ├── components.css # Nav, buttons, cards, footer, forms, prose
│ │ │ ├── effects.css # Aurora, glow, animations, spotlight, typewriter
│ │ │ └── main.css # Placeholder; actual CSS is concatenated in head.html
│ │ ├── Icons/ # Favicon and touch icons
│ │ └── js/main.js # Client-side interactions & data fetching
│ ├── layouts/
│ │ ├── _default/
│ │ │ ├── baseof.html # Root layout (inline JS config + main.js)
│ │ │ ├── list.html # List pages (books, etc.)
│ │ │ └── single.html # Single content pages
│ │ ├── index.html # Homepage (composes partials)
│ │ └── partials/ # Modular reusable components
│ │ ├── head.html # CSS concat via Hugo pipeline
│ │ ├── header.html # Fixed nav with scroll blur
│ │ ├── footer.html # Dynamic footer columns
│ │ ├── hero.html # Full-viewport hero with aurora
│ │ └── sections/ # Homepage sections
│ │ ├── github.html
│ │ ├── pypi.html
│ │ ├── books.html
│ │ └── booking.html
│ └── theme.toml
├── .github/workflows/ # CI/CD definitions
│ ├── hugo.yml # Build and deploy to GitHub Pages
│ └── sync-pypi.yml # Daily PyPI stats sync
├── hugo.toml # Hugo site configuration
├── pyproject.toml # Python project metadata and dependencies
└── uv.lock # Locked Python dependency tree
Prerequisite: Hugo Extended 0.125.0 (or compatible). Download from gohugoio/hugo/releases.
# Start local development server with live reload
hugo server -D
# Build production site (outputs to ./public)
hugo --gc --minify
# Build with explicit baseURL
hugo --gc --minify --baseURL "https://techbend.dev/"--gc— cleans up unused cached resources--minify— minifies HTML, CSS, JS, JSON-D— includes draft content (dev only)
Prerequisite: Python 3.12 and uv (or pip with pyproject.toml).
# Install dependencies (using uv)
uv sync
# Or using pip
pip install -e .
# Activate virtualenv
source .venv/bin/activate
# Discover packages on PyPI
python scripts/discover_packages.py
# Sync PyPI download stats from BigQuery
python scripts/sync_stats_bigquery.py
# Sync PyPI download stats from ClickPy (ClickHouse)
python scripts/sync_stats_clickpy.py
# Sync GitHub profile and pinned repos
python scripts/sync_github.py
# Generate manifest.json (combines GitHub + PyPI data)
python scripts/generate_manifest.pyEnvironment variable required for BigQuery:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/gcp-service-account.jsonA local service account key exists at env/techbend-89ea5a3e24a2.json for development. This file is gitignored and must never be committed.
- Use Go template syntax consistently:
{{ .Variable }} - Indent with 2 spaces inside templates
- Use
{{ if .Site.Params.showX }}pattern for conditional sections - Prefer
whereandfirstfor page queries (e.g.,{{ range first 3 (where .Site.RegularPages "Section" "books") }}) - External menu links are detected with
hasPrefix .URL "http"and receivetarget="_blank" rel="noopener" - Active nav state uses
$.IsMenuCurrent "main" . - Modular architecture: The homepage (
index.html) should only compose partials; no direct markup. Each section lives inpartials/sections/. - No hardcoded text: All user-facing strings must come from
.Site.Paramsor content front matter.
- No external CSS frameworks. All styles are hand-crafted in
themes/techbend/assets/css/. - The CSS is split into 6 modules and concatenated via Hugo's
resources.Concat:reset.css— minimal modern resettokens.css— CSS custom properties for colors, spacing, typography, radii, shadowsbase.css— body, headings, links, scrollbar, selectionlayout.css— containers, grids, flex utilities, responsive breakpointscomponents.css— nav, buttons, cards, badges, footer, forms, prose contenteffects.css— aurora blobs, gradient text, spotlight hover, glow borders, reveal animations, shimmer skeletons, typewriter
- Both
lightanddarkthemes are defined as complete token sets on[data-theme="dark"]and[data-theme="light"]. - Use CSS variables (
var(--bg),var(--accent), etc.) for all colors and metrics. - Mobile breakpoint is at
768px. - The
spotlight-cardclass creates a radial gradient that follows the cursor on hover.
- Single-file architecture: all client JS lives in
themes/techbend/assets/js/main.js - Modular by function: each feature has an
init*orfetch*function - Uses
DOMContentLoadedevent for initialization - Fetches local
/data/manifest.jsonfor aggregated GitHub + PyPI data, and the PyPI JSON API directly for package details - Theme toggle is a custom button (
#themeToggle) that swapsdata-themebetweendarkandlight, backed bylocalStorage - Mobile menu toggles the
.openclass on#mobileNav - Scroll-triggered nav blur adds
.scrolledto#siteNavwhenscrollY > 20
- Follows PEP 8 with some project conventions:
- Type hints used for function signatures (e.g.,
def get_pypi_packages(username: str) -> list[str]) - Docstrings are concise and descriptive
UPPER_SNAKE_CASEfor module-level constantssnake_casefor functions and variables- Uses
pathlib.Pathfor filesystem operations - Uses
datetime.now(timezone.utc)for timezone-aware timestamps
- Type hints used for function signatures (e.g.,
There are no automated tests in this project. There is no pytest, unittest, or tox configuration.
Manual verification steps:
- Run
hugo server -Dand visually inspect the site athttp://localhost:1313 - Check browser console for JS errors after page load
- Verify theme toggle works (dark ↔ light)
- Verify mobile menu opens/closes at narrow viewport widths
- Run
python scripts/discover_packages.pyand confirm it returns the expected package list - Run
python scripts/sync_stats_bigquery.py(with valid GCP credentials) and verify JSON/CSV files are written tostatic/data/ - Run
python scripts/sync_github.pyand verifystatic/data/github.jsonis updated - Run
python scripts/generate_manifest.pyand verifystatic/data/manifest.jsonis created
The Python scripts form a small ETL pipeline with multiple data sources:
discover_packages.py
│
▼
Scrape PyPI user page for package names
│
├──────────────┬────────────────┐
▼ ▼ ▼
sync_stats_bigquery.py sync_stats_clickpy.py sync_github.py
Query BigQuery Query ClickPy Query GitHub API
(public dataset) (ClickHouse) + profile scrape
│ │ │
└──────────────┴────────────────┘
│
▼
static/data/pypi/{package}.json
static/data/pypi/{package}.csv
static/data/github.json
static/data/pypi.json
│
▼
generate_manifest.py
│
▼
static/data/manifest.json
discover_packages.pyscrapeshttps://pypi.org/user/tavallaie/usingrequests+BeautifulSoupsync_stats_bigquery.pyqueriesbigquery-public-data.pypi.file_downloadswith smart incremental sync: discovers each package's first/last download date, then on subsequent runs only fetches new dayssync_stats_clickpy.pyqueries the ClickPy ClickHouse instance (sql-clickhouse.clickhouse.com) for per-package download statssync_github.pyscrapes the GitHub profile page for pinned repos and contribution count, then enriches with the GitHub REST APIgenerate_manifest.pyreadsgithub.jsonandpypi.json, selects the top 6 most-downloaded packages, and writes a combinedmanifest.jsonfor the frontend- Output columns (PyPI):
day,version,system,python_version,installer,downloads - The frontend fetches
manifest.jsonfor hero stats, GitHub pinned projects, and PyPI top packages
This pipeline runs automatically via .github/workflows/sync-pypi.yml every day at 06:00 UTC.
Triggered by: push to main or manual workflow_dispatch.
Workflow: .github/workflows/hugo.yml
- Install Hugo Extended
0.125.0onubuntu-latest - Checkout with
submodules: recursiveandfetch-depth: 0 - Configure GitHub Pages
- Build:
hugo --gc --minify --baseURL <pages-url>/ - Upload
./publicas artifact - Deploy via
actions/deploy-pages@v4
Triggered by: cron 0 6 * * * (daily 6 AM UTC) or manual dispatch.
Workflow: .github/workflows/sync-pypi.yml
- Set up Python 3.12
- Install BigQuery dependencies via
pip - Write GCP service account key from
secrets.GCP_SA_KEYto/tmp/gcp-key.json - Run
python scripts/sync_stats.py - Clean up credentials (runs
always()) - Commit and push changes in
static/data/directory with bot identity
Note: The workflow currently references
scripts/sync_stats.py, which no longer exists. The equivalent script isscripts/sync_stats_bigquery.py.
- GCP Service Account Key: The local file
env/techbend-89ea5a3e24a2.jsonis excluded from Git via.gitignore. In CI, the key is written fromsecrets.GCP_SA_KEYand deleted in analways()step. - No Server-Side Execution: The deployed site is purely static HTML/CSS/JS. There is no server-side code running in production.
- Client-Side API Calls: JavaScript calls public APIs (GitHub API, PyPI API) from the user's browser. No API keys are exposed in frontend code.
- Subresource Integrity: Hugo's asset pipeline generates SRI hashes for CSS and JS in
baseof.html. - External Links: All external menu links receive
rel="noopener"to preventwindow.openerexploits. - No User Input Forms: There is no active contact form. The contact page uses static cards/links (if present). There is no active Formspree configuration (
formspreeIdis empty inhugo.toml).
- The custom theme is named
techbendand lives entirely underthemes/techbend/ - There are no project-level layout overrides in
/layouts/ - The homepage (
layouts/index.html) is a composition of partials only; it includes no direct markup - Custom assets are processed through Hugo pipelines:
- CSS: 6 files are concatenated via
resources.Concat, then minified and fingerprinted:{{ $css := slice $reset $tokens $base $layout $components $effects | resources.Concat "css/main.css" | resources.Minify | fingerprint }} - JS:
resources.Get "js/main.js" | resources.Minify | fingerprint
- CSS: 6 files are concatenated via
- Site parameters are exposed to JavaScript via an inline script in
baseof.html(e.g.,window.GITHUB_USER,window.PYPI_USER) theme.tomlspecifiesmin_version = "0.118.0"for Hugo compatibility- Light/Dark mode: Controlled by
data-themeattribute on<html>. A custom button (#themeToggle) swaps the attribute betweendarkandlight, backed bylocalStorage. The active theme is also set via an inline script inhead.htmlto prevent flash-of-unstyled-content.
| Parameter | Value | Purpose |
|---|---|---|
baseURL |
https://techbend.dev |
Production domain |
theme |
techbend |
Custom theme directory name |
params.author |
Ali Tavallaie |
Site author |
params.description |
AI Software Engineer & Open Source Maintainer |
Default meta description |
params.gravatarEmail |
a.tavallaie@gmail.com |
Gravatar email for avatar |
params.github |
tavallaie |
GitHub username for API calls |
params.githubOrg |
techbend |
GitHub org name |
params.pypiUser |
tavallaie |
PyPI username for scraping |
params.email |
ali@techbend.dev |
Contact email |
params.calLink |
https://cal.com/tavallaie |
Cal.com booking URL |
params.sponsorLink |
https://github.com/sponsors/tavallaie |
GitHub Sponsors URL |
params.blogLink |
https://blog.techbend.dev |
External blog link |
params.showGithub |
true |
Toggle GitHub sections |
params.showPyPI |
true |
Toggle PyPI sections |
params.showBooks |
true |
Toggle books section |
params.showResume |
true |
Toggle resume page |
params.showSponsor |
true |
Toggle sponsor button |
params.showCalendar |
true |
Toggle calendar/booking button |
params.hero.name |
Ali Tavallaie |
Hero section name |
params.hero.subtitle |
(defaults to description) |
Hero subtitle text |
params.hero.statusText |
Available for consulting |
Status badge text |
params.hero.statusColor |
success |
Badge color for status |
params.hero.avatarSize |
200 |
Avatar image size |
params.sections.*.title |
various | Section headings |
params.footer.columns |
array | Footer link columns (dynamic) |
params.footer.copyright |
Ali Tavallaie. Built with Hugo & caffeine. |
Footer copyright text |
params.footer.showSponsor |
true |
Toggle sponsor link in footer |
Python dependencies:
beautifulsoup4>=4.14.3clickhouse-connect>=0.15.1db-dtypes>=1.5.1google-cloud-bigquery>=3.41.0pandas>=2.3.3requests>=2.33.1
Add a new section to the homepage:
- Create a new partial in
themes/techbend/layouts/partials/sections/my-section.html - Add corresponding params to
hugo.tomlunder[params.sections.mySection] - Include the partial in
themes/techbend/layouts/index.html
Add a new content page: Create a Markdown file in content/ with front matter. It will automatically use themes/techbend/layouts/_default/single.html unless a custom layout is specified.
Modify styles: Edit the relevant module in themes/techbend/assets/css/ (e.g., components.css for UI elements, effects.css for animations). Use existing CSS variables for colors and spacing. Only main.css is a placeholder — the actual CSS is concatenated via Hugo's pipeline in head.html.
Modify client-side behavior: Edit themes/techbend/assets/js/main.js. The file is organized into discrete initialization functions called from a single DOMContentLoaded listener.
Add or update PyPI package tracking: The package list is dynamically discovered by scraping the PyPI user page. No manual configuration is needed. If a package is missing, verify it appears on https://pypi.org/user/tavallaie/ and re-run sync_stats_bigquery.py or sync_stats_clickpy.py.
Regenerate the frontend manifest: After updating GitHub or PyPI data, run python scripts/generate_manifest.py to update static/data/manifest.json with the latest combined data.
Update Hugo version: Change HUGO_VERSION in .github/workflows/hugo.yml and update min_version in themes/techbend/theme.toml if necessary.
Change theme colors: Edit themes/techbend/assets/css/tokens.css. Both [data-theme="dark"] and [data-theme="light"] have complete token sets. Modify any --* variable to change colors, spacing, or shadows across the entire site.
- Hugo documentation: https://gohugo.io/documentation/
- BigQuery public PyPI dataset: https://console.cloud.google.com/marketplace/product/gcp-public-data-pypi
- ClickPy (ClickHouse PyPI data): https://clickpy.clickhouse.com/
- PyPI JSON API: https://docs.pypi.org/api/json/
- GitHub API (users/repos): https://docs.github.com/en/rest/repos/repos#list-repositories-for-a-user