Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 85 additions & 74 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# jonathanperis.github.io — Claude Code Guide

Personal developer portfolio built with Next.js 16, deployed as a static site to GitHub Pages.
Personal developer portfolio built with Astro 6 and React 19, deployed as a static site to GitHub Pages.

**Live:** https://jonathanperis.github.io/

Expand All @@ -10,90 +10,101 @@ Personal developer portfolio built with Next.js 16, deployed as a static site to

| Technology | Purpose |
|-----------|---------|
| Next.js 16.2 | App Router, static export |
| React 19.2 | Component rendering |
| TypeScript 5 | Type safety |
| Tailwind CSS 4 | Utility-first styling |
| GitHub GraphQL API | Fetch pinned repos at build time |
| Google Analytics 4 | Traffic tracking |
| Astro 6 | Static site build and GitHub Pages export |
| React 19 | Hydrated interactive portfolio UI |
| TypeScript 6 | Strict type checking through `astro/tsconfigs/strict` |
| Tailwind CSS 4 | Custom low-glare terminal-style design system via `@tailwindcss/vite` |
| GitHub GraphQL + REST APIs | Build-time repository and GitHub Pages URL discovery |
| Google Analytics 4 | Traffic and CTA event tracking when `PUBLIC_GA_ID` is set |
| Bun | Local and CI package manager/script runner |

---

## Build Commands

```sh
npm run dev # Dev server on :3000
npm run build # Static export to ./out
npm run lint # ESLint (Next.js web vitals + TypeScript)
npm start # Production server (local only)
bun install # install dependencies
bun run dev # Astro dev server on :4321
bun run lint # astro check
bun run build # static export to ./out
bun run preview # preview the built ./out artifact
```

For live repository data during local builds:

```sh
GITHUB_TOKEN=$(gh auth token) PUBLIC_GA_ID=G-35CN95481D bun run build
```

---

## Architecture

```
Server Components (RSC)
├── app/page.tsx # Async: fetches GitHub repos, renders Portfolio
├── app/layout.tsx # Root: metadata, fonts, GA, JSON-LD
└── app/resume/layout.tsx # Resume metadata

Client Components ("use client")
├── Portfolio # Main interactive UI (terminal, typing, scroll)
├── ResumePage # Print-optimized resume with PDF download
├── Analytics # GA4 script injection
└── JsonLd # Schema.org structured data

Data Layer
├── lib/github.ts # GraphQL API + fallback repos
└── lib/data.ts # PROFILE, SKILLS, EXPERIENCES, FEATURED_PROJECTS
```text
Astro pages/layouts
├── src/pages/index.astro # Build-time fetchRepos(), renders Portfolio with client:load
├── src/pages/resume.astro # Print-optimized resume route
└── src/layouts/RootLayout.astro # HTML shell, metadata, fonts, JSON-LD, analytics

Interactive island
└── src/components/Portfolio.tsx # Main React UI: hero, profile, stack, experience, Workbench, terminal

Astro components
├── src/components/Analytics.astro # Conditional GA4 loader from PUBLIC_GA_ID
└── src/components/JsonLd.astro # Schema.org Person JSON-LD

Data layer
├── src/lib/github.ts # GitHub GraphQL + REST client with fallback repo data
└── src/lib/data.ts # PROFILE, AVAILABILITY, SKILLS, EXPERIENCES, EDUCATION, SOCIALS
```

---

## Key Patterns

- **Static export** — `output: "export"` in next.config.ts, no server at runtime
- **Build-time data fetching** — GitHub API called during `npm run build`
- **Fallback data** — Hardcoded repos in `github.ts` if API fails
- **Terminal easter egg** — CLI emulator with Konami code trigger
- **Typing animation** — Custom `useTyping()` hook for role cycling
- **Print optimization** — Resume page with A4 CSS, black/white media queries
- **SEO** — JSON-LD (Schema.org Person), OG/Twitter meta, sitemap.xml
- **Cache headers** — Static assets: `max-age=31536000, immutable`
- **Static export** — `astro.config.ts` sets `outDir: 'out'`; GitHub Pages deploys the generated artifact.
- **Build-time GitHub data** — `src/pages/index.astro` calls `fetchRepos()` during `bun run build`; the deployed browser page does not call GitHub APIs.
- **Pinned + ledger model** — `src/lib/github.ts` fetches GitHub profile pinned repos and owned public non-fork repos, excludes metadata repos, preserves pinned order, and removes pinned repos from the lower ledger.
- **Pages URL enrichment** — REST `GET /repos/jonathanperis/{repo}/pages` provides `pagesUrl`; standard `https://jonathanperis.github.io/<repo>/` homepage URLs are fallback Pages links.
- **Fallback data** — Hardcoded repos keep local builds and PR checks working when `GITHUB_TOKEN` is absent.
- **Single profile source** — `src/lib/data.ts` powers the portfolio, resume, terminal snippets, and JSON-LD.
- **Terminal easter egg** — Konami code opens an in-page terminal. `runCmd()` in `Portfolio.tsx` handles `help`, `about`, `stack`, `contact`, `neofetch`, `git log`, `ls`, `cat availability.txt`, `whoami`, `pwd`, `date`, `sudo hire me`, `echo`, `clear`, `exit`, and `quit`.
- **SEO** — `RootLayout.astro` emits canonical, Open Graph, Twitter, icon, manifest, alternate-language, JSON-LD, and font tags; `public/robots.txt` and `public/sitemap.xml` are included.

---

## Project Structure

```
```text
jonathanperis.github.io/
├── app/
│ ├── page.tsx # Home page (server component)
│ ├── portfolio.tsx # Main UI (client component)
│ ├── layout.tsx # Root layout + metadata
│ ├── globals.css # Tailwind + custom theme + animations
├── src/
│ ├── pages/
│ │ ├── index.astro
│ │ └── resume.astro
│ ├── components/
│ │ ├── analytics.tsx # GA4 integration
│ │ └── json-ld.tsx # Structured data
│ │ ├── Portfolio.tsx
│ │ ├── Analytics.astro
│ │ └── JsonLd.astro
│ ├── layouts/
│ │ └── RootLayout.astro
│ ├── lib/
│ │ ├── github.ts # GitHub GraphQL client + fallback
│ │ └── data.ts # Static profile/skills/experience data
│ └── resume/
│ ├── layout.tsx # Resume layout
│ └── page.tsx # Print-optimized resume
│ │ ├── github.ts
│ │ └── data.ts
│ └── styles/
│ └── globals.css
├── public/
│ ├── cv_jonathan_peris.pdf # Downloadable CV
│ ├── manifest.json # PWA manifest
│ ├── cv_jonathan_peris.pdf
│ ├── manifest.json
│ ├── robots.txt / sitemap.xml
│ └── favicon.svg / apple-touch-icon.png
├── next.config.ts # Static export, cache headers
├── tsconfig.json # strict, ES2017, @/* alias
├── postcss.config.mjs # Tailwind CSS v4
├── wiki/
├── astro.config.ts
├── tsconfig.json
├── package.json
└── .github/workflows/
├── build-check.yml # PR build check (lint + build)
├── main-release.yml # GitHub Pages deploy on push to main
└── codeql.yml # Security analysis (JS/TS)
├── build-check.yml
├── main-release.yml
└── codeql.yml
```

---
Expand All @@ -102,41 +113,41 @@ jonathanperis.github.io/

| Variable | Purpose |
|----------|---------|
| `GITHUB_TOKEN` | GitHub API auth (provided by Actions) |
| `NEXT_PUBLIC_GA_ID` | GA4 tracking ID (G-35CN95481D) |
| `GITHUB_TOKEN` | GitHub API auth for GraphQL repo data and REST Pages URL lookup; provided by Actions |
| `PUBLIC_GA_ID` | GA4 tracking ID consumed by `src/components/Analytics.astro` (`G-35CN95481D` in workflows) |

---

## CI/CD

| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `build-check.yml` | Pull requests to main | Lint + build validation |
| `main-release.yml` | Push to main / manual dispatch | Build upload deploy to GitHub Pages |
| `codeql.yml` | Push, PRs, weekly (Mon 06:00 UTC) | JavaScript/TypeScript security scanning |
| `build-check.yml` | Pull requests to `main`, manual dispatch | Bun install, `astro check`, Astro build |
| `main-release.yml` | Push to `main`, manual dispatch | Build `out/`, upload artifact, deploy GitHub Pages |
| `codeql.yml` | Push, PRs, weekly Monday 06:00 UTC | JavaScript/TypeScript security scanning |

- **Dependabot:** Weekly npm + GitHub Actions updates
- **Dependabot:** Weekly npm and GitHub Actions updates
- **Merge strategy:** Rebase only (squash and merge commits disabled)
- **Branch protection:** Main branch is protected; all changes go through PRs
- **Auto-merge:** Enabled for Dependabot PRs
- **Branch protection:** Main branch is protected; changes go through PRs
- **Community health files:** CODE_OF_CONDUCT, CONTRIBUTING, SECURITY, and SUPPORT live in the [`.github` repo](https://github.com/jonathanperis/.github); do not duplicate them here

---

## Development Workflow

1. **Sync main first:** `git fetch origin main && git checkout main && git pull origin main`
2. Create a feature branch from `main`
3. Make changes and push
4. **Before opening a PR:** fetch and pull main again to ensure no conflicts
5. Open a PR targeting `main` — CI runs lint + build automatically
6. After review and green checks, rebase-merge the PR
7. `main-release.yml` triggers automatically on push to main
1. Sync main first: `git fetch origin main && git switch main && git pull --ff-only origin main`
2. Create a branch from `main`
3. Make changes and run `bun run lint` and `bun run build`
4. Push the branch and open a PR targeting `main`
5. Watch PR checks and resolve any failures
6. Rebase-merge when checks/review are green and merge is authorized
7. Watch `main-release.yml`, then verify the live GitHub Pages route(s)

---

## Repository Conventions

- **GitHub operations:** Always use `gh` CLI
- **Community health files** (CODE_OF_CONDUCT, CONTRIBUTING, SECURITY, SUPPORT) live in the [`.github` repo](https://github.com/jonathanperis/.github) — do not duplicate them here
- **PR strategy:** Branch + PR for all changes, rebase merge only
- **Commit style:** Conventional commits (`feat:`, `fix:`, `chore:`, `docs:`)
- Use the `gh` CLI for GitHub operations.
- Use Bun, not npm, for install/build commands in this repo.
- Keep README/wiki/Claude docs aligned with the Astro source tree (`src/...`) and workflow files.
- Commit messages should use Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`).
56 changes: 32 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,47 @@

[![Build Check](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/build-check.yml/badge.svg)](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/build-check.yml) [![Main Release](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/main-release.yml/badge.svg)](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/main-release.yml) [![CodeQL](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/codeql.yml/badge.svg)](https://github.com/jonathanperis/jonathanperis.github.io/actions/workflows/codeql.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

**[Live demo →](https://jonathanperis.github.io/)** | **[Documentation →](CLAUDE.md)**
**[Live demo →](https://jonathanperis.github.io/)** | **[Contributor guide →](CLAUDE.md)** | **[Wiki →](wiki/index.md)**

---

## About

Astro portfolio with a static export for GitHub Pages. It fetches the repositories pinned on Jonathan's GitHub profile plus public, non-fork repositories from the GitHub GraphQL API at build time, resolves live GitHub Pages links through the REST API, and renders them in a terminal-themed UI.
Astro portfolio with a static export for GitHub Pages. It fetches the repositories pinned on Jonathan's GitHub profile plus owned public, non-fork repositories from the GitHub GraphQL API at build time, resolves live GitHub Pages links through the REST API, and renders them in a terminal-themed UI.

The site includes a print-optimized resume page, SEO metadata, analytics, and a Konami code easter egg. The same shared data powers the on-page resume and the dedicated `/resume` route.

It is built to stay simple to deploy: build locally, export statically, and publish through GitHub Actions.
The site includes a print-optimized `/resume` route, SEO metadata, JSON-LD, GA4 analytics, a PWA manifest, and a Konami-code terminal easter egg. Profile, skills, education, availability, social links, and experience data live in `src/lib/data.ts` and power both the portfolio and resume.

## Tech Stack

| Technology | Version | Purpose |
|-----------|---------|---------|
| Astro | 6 | Static site build and GitHub Pages export |
| React | 19 | Interactive portfolio UI |
| TypeScript | Latest | Type safety |
| Tailwind CSS | v4 | Styling system |
| GitHub GraphQL + REST APIs | v4 / REST | Fetches repositories and live Pages URLs at build time |
| Google Analytics 4 | GA4 | Traffic and engagement analytics |
| Technology | Version / source | Purpose |
|-----------|------------------|---------|
| Astro | `^6` | Static site build and GitHub Pages export |
| React | `^19` | Interactive portfolio UI (`client:load`) |
| TypeScript | `^6` with `astro/tsconfigs/strict` | Type safety |
| Tailwind CSS | `^4` via `@tailwindcss/vite` | Styling system |
| GitHub GraphQL + REST APIs | GraphQL + REST `2022-11-28` | Fetches repositories and live Pages URLs at build time |
| Google Analytics 4 | `PUBLIC_GA_ID` | Traffic and engagement analytics |
| Bun | Workflow/local package manager | Install, lint, and build commands |

## Features

- Workbench major cards sourced from GitHub profile pinned repositories
- Dynamic "Other GitHub repos" ledger from GitHub GraphQL API (public, non-fork repos)
- Dynamic "Other GitHub repos" ledger from GitHub GraphQL API (owned public, non-fork repos)
- Live GitHub Pages links resolved at build time via GitHub REST API
- Terminal-themed dark UI with typing animations and scroll effects
- Print-optimized resume page with download support
- PWA manifest and SEO optimizations
- Konami code easter egg
- Static export deployed to GitHub Pages
- Terminal-themed dark UI with scroll/reveal effects and responsive project cards
- Print-optimized `/resume` route with browser PDF download support
- PWA manifest, sitemap, robots.txt, Open Graph, Twitter, and JSON-LD metadata
- Google Analytics 4 loaded only when `PUBLIC_GA_ID` is set
- Konami-code terminal easter egg
- Static export deployed to GitHub Pages from `out/`

## Getting Started

### Prerequisites

- Node.js 22+, Bun
- Node.js 22+
- Bun
- Optional: GitHub CLI (`gh`) for providing a local `GITHUB_TOKEN`

### Quick Start

Expand All @@ -53,15 +55,21 @@ bun install
bun run dev
```

Open http://localhost:4321
Open <http://localhost:4321>.

To build with live repository data instead of fallback data:

```bash
GITHUB_TOKEN=$(gh auth token) bun run build
```

## CI/CD

| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `build-check.yml` | Pull requests to main | Lint + build check |
| `main-release.yml` | Push to main / manual | Build and deploy to GitHub Pages |
| `codeql.yml` | Push, PRs, weekly schedule | JavaScript/TypeScript security analysis |
| `build-check.yml` | Pull requests to `main`, manual dispatch | Bun install, `astro check`, and Astro build |
| `main-release.yml` | Push to `main`, manual dispatch | Build `out/`, upload Pages artifact, deploy GitHub Pages |
| `codeql.yml` | Push, PRs, weekly Monday 06:00 UTC | JavaScript/TypeScript security analysis |

Dependabot monitors npm and GitHub Actions dependencies weekly.

Expand Down
Loading