Personal website for Nidhin Dileepkumar at nidhy.dev.
Built with Astro 6, static output, deployed via GitHub Pages from crackcode09/crackcode09.github.io.
- Framework: Astro 6 (static, no SSR)
- Content: Astro Content Layer API (
src/content.config.ts) - Styling: Scoped CSS with design tokens in
src/styles/tokens.css - Components:
src/components/— Header, Footer, Base (layout), Label, Button, Chip, CodeBlock, Badge, Panel - Fonts: Loaded in Base.astro via Google Fonts — display (Major Mono Display), serif (Fraunces), mono (Space Mono)
- Design system:
D:\nidhin.dev\content\brand\Nidhin Design System\— canonical source of truthguidelines/design-kit.html— visual reference (self-contained)tokens/— colors, typography, shape, spacing token filescomponents/core/— component specs (Button, Label, Chip, Panel, CodeBlock, Badge, Quote)brand/claude-design-brief.md— paste-ready brief for any new Claude design task
Never commit directly to dev or main. Always use a feature branch.
feature/— new pages, components, or functionalityfix/— bug fixescontent/— writing posts, copy changes
- Create feature branch off
dev - Commit changes to feature branch
- Open PR targeting
dev— CI runs build check - Once CI passes, merge PR into
dev - Open PR from
dev→mainwith auto-merge using regular merge:gh pr merge --auto --merge - CI passes → auto-merges to
main→ GitHub Pages deploys automatically
No manual touching of main ever. CI/auto-merge handles it.
Why
--mergenot--squashfor dev→main: Squash rewrites commit SHAs, causing history divergence. Every subsequent dev→main PR then conflicts even with identical content. Regular merge preserves the commit graph permanently.
Key tokens from src/styles/tokens.css:
--blood— brand red (primary accent)--signal— green (course accent)--gold— gold (capstone accent)--ink,--ink-60,--ink-30,--ink-12— text hierarchy--paper,--paper-2,--paper-soft— background hierarchy--border—2px solid var(--ink)(hard border, no radius)--container-page: 1120px— max content width--space-1through--space-7— spacing scale
Card shadow style: box-shadow: 8px 8px 0 var(--accent) — hard offset, no blur.
-
Write a brief — create
docs/courses/drafts/[course-slug].md- Copy the template from
docs/courses/drafts/_template.md - Fill in: title, audience, modules, projects, stats, repo URL, accent color, selling points
- This folder is gitignored — drafts stay local, never pushed to GitHub
- Copy the template from
-
Open a Claude session and say:
"Build the course page for
docs/courses/drafts/[course-slug].md" -
Claude will:
- Read the draft
- Create
src/content/courses/[slug].json(metadata — listing card auto-updates) - Create
src/pages/courses/[slug].astro(full bespoke landing page)
-
Review — run
npm run dev, check/coursesand/courses/[slug] -
Commit and deploy
- Use
src/pages/courses/zero-to-ai.astroas the structural reference - Import path convention:
../../layouts/Base.astro,../../components/Header.astro, etc. - Accent color maps:
green → var(--signal),red → var(--blood),gold → var(--gold) - All CSS is page-scoped — no global style changes
- Match the hard-offset-shadow card language and mono/serif/display font hierarchy
Each course JSON (src/content/courses/[slug].json) must match:
{
"title": "string",
"slug": "string (matches filename)",
"description": "string (used on listing card)",
"status": "live | coming-soon | draft",
"label": "string (small badge text)",
"accent": "green | red | gold | default",
"modules": 0,
"projects": 0,
"duration": "string e.g. 8–16 weeks",
"cost": "string e.g. $5 or free",
"repo": "https://github.com/..."
}draft status courses are excluded from the listing. coming-soon shows as a muted non-linked card.
-
Draft locally — create
docs/writing/drafts/[slug].md- Copy the template from
docs/writing/drafts/_template.md - This folder is gitignored — stays local, never pushed to GitHub
- Copy the template from
-
When ready — create
src/content/writing/[slug].mdwith frontmatter below -
Set
published: truewhen you want it live
---
title: "Post title"
date: 2026-06-13
tag: "systems | ai | ie | build"
excerpt: "Max 160 chars — shown on listing and meta."
published: false # set true when ready
readingTime: 5 # optional, minutes
featured: false # optional, shows at top of list
----
Write a brief — create
docs/projects/drafts/[slug].md- Copy the template from
docs/projects/drafts/_template.md - Fill in: name, lift color, tags, blurb, metrics, case study outline
- This folder is gitignored — stays local, never pushed to GitHub
- Copy the template from
-
Create the JSON —
src/content/projects/[slug].json- Copy the JSON block from your draft
- Set
status: "live"andhrefto the case study URL when ready
-
Review — run
npm run dev, check/projectsand homepage -
Commit and deploy
Each project JSON (src/content/projects/[slug].json) must match:
{
"name": "lowercase project name",
"slug": "url-safe-slug",
"lift": "red | green | gold | default",
"tags": ["tag1", "tag2"],
"blurb": "2-sentence max. What you built. Measured outcome.",
"href": "# or /writing/case-study-slug",
"status": "live | coming-soon | draft",
"featured": true
}featured: true→ shows on homepage (max 3 displayed)status: "draft"→ excluded from listingstatus: "coming-soon"→ muted non-linked card on/projects- Lift color guide:
red= savings/fix,green= build/growth,gold= capstone/leadership
npm run dev # dev server at http://localhost:4321
npm run build # production build to dist/
npm run preview # preview built output