CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Personal portfolio/resume website for Shreyas Gokhale. Built on the Astro Nano theme with Astro 4, Tailwind CSS, TypeScript, and MDX. No frontend frameworks.

Commands

CommandPurpose
npm run devDev server at localhost:4321
npm run buildType-check (astro check), build, then generate Pagefind search index
npm run lintESLint
npm run lint:fixESLint with auto-fix

Architecture

Internationalization (i18n)

The site supports English (default, no prefix) and German (/de/ prefix). Configured via Astro’s built-in i18n in astro.config.mjs.

Routing: English at /, German at /de/. Blog-style content (Scribbles, Subroutines, GSoC, FAQ, Tags) is English-only.

Translation module:

  • src/i18n/ui.ts — All UI strings keyed by locale (en/de)
  • src/i18n/utils.ts — Helpers: getLangFromUrl(), useTranslations(), getLocalizedPath(), hasLocalizedVersion(), getAlternateLocale(), getLocaleForDateFormatting()

Page architecture: Shared page components in src/components/pages/ accept a lang prop. Thin wrappers in src/pages/ (English) and src/pages/de/ (German) render them.

Content translation: Side-by-side files with .de.md suffix (e.g., gropyus.md + GROPYUS.de.md). German files have lang: de in frontmatter. Collections are filtered by (e.data.lang ?? "en") === lang.

Language switcher: src/components/LanguageSwitcher.astro — flag emoji in nav bar. Always visible; on English-only pages, links to /de/ home.

Adding a new German page:

  1. Create shared component in src/components/pages/
  2. Create thin wrapper in src/pages/de/
  3. Add translated content files with .de.md suffix and lang: de

Content

All content collections live in src/content/ with schemas defined in src/content/config.ts.

Key Files

  • src/consts.ts - Site metadata, social links, nav items, hub cards, and RESUME_SECTIONS order
  • src/types.ts - Shared TypeScript types (Site, Metadata, Socials)
  • src/lib/utils.ts - Utility functions (cn, formatDate, dateRange, readingTime)
  • src/remark-highlight.js - Custom remark plugin for ==highlight== syntax
  • src/lib/skillIcons.ts - Skill icon mappings
  • src/lib/tags.ts - Tag domain mapping (getTagDomain, getTagClasses, DOMAIN_LABELS)

Content Collections

Defined via Zod schemas in config.ts.

CollectionSchema fieldsNotes
workcompany, role, dateStart, dateEnd, location, tags?, draft?, lang?One .md per job. German: .de.md suffix
educationcompany, role, dateStart, dateEnd, tags?, lang?One .md per degree
projectstitle, description, date, draft?, demoURL?, repoURL?, tags?, lang?One .md per project
aboutlang?about.md (EN) and about-de.md (DE), body used as resume summary
skillscategory, domain?, order?, lang?One .md per category (e.g. embedded.md). Body is a markdown list of skills. domain maps to tag colors. order controls display order.
languageslanguage, proficiency, order?, lang?One .md per language (e.g. english.md). order controls display order.
scribblestitle, description?, date, tags?, draft?Blog-style posts
subroutinestitle, description?, date, tags?, draft?Technical writeups
gsoctitle, description?, date, tags?, draft?GSoC 2020 posts
nowupdated?Single now page

Resume Section Order

The resume page (src/pages/resume/index.astro) renders sections dynamically based on RESUME_SECTIONS in src/consts.ts. To reorder, add, or remove resume sections, edit this array:

export const RESUME_SECTIONS: string[] = [
  "summary", "skills", "work", "education", "languages", "projects",
];

Valid keys: summary, skills, languages, work, education, projects.

Path Aliases

@* maps to ./src/* (configured in tsconfig.json). Use @components/, @lib/, @layouts/, @consts, @types, etc.

Styling

  • Tailwind with @tailwindcss/typography plugin
  • Dark mode via class strategy (darkMode: ["class"])
  • Fonts: Inter (sans), Lora (serif) via Fontsource; Sentient (display) via Fontshare CDN
  • Print-specific CSS classes used in homepage layout

ESLint Rules

  • Semicolons required, double quotes enforced, template literals allowed