How it's built
Every number on this sheet comes from this build or from your own request. None of it is typed by hand.
1 · Your request, up close
How this page reached you
Connecting…
- Last deploy
- 9 Oct, 10:50 pm IST, from main
- Visits today
- –
- Pipeline
- git push · main → GitHub Actions → wrangler deploy
Revision4.7
Buildca45814
Statusstatic
2 · How it got here
git push · main → GitHub Actions → wrangler deploy · see the run
3 · What shipped with this build
- ca45814Cards: round case study arrow instead of the strip; honest 300+ sign-ups wording; bigger photo9 Oct 2026
- 64152e6Project pages: What it does as a simple ruled list9 Oct 2026
- 603a6f3Plainer headline, clearer GFG line, case-study strip on cards, key decisions and what I'd do differently on project pages9 Oct 2026
- 50869dfSearch: sitemap, robots.txt, llms.txt, canonical links and who-I-am data for Google9 Oct 2026
- edcd2bePhones: photo and role above the name, open-to line, stack after the buttons; no sideways scroll on small screens9 Oct 2026
- 7644619Menu as bordered buttons; side panel stays in view and its grid reaches the screen edge; tidier profile card on phones9 Oct 2026
- 13d19a7Keep design previews out of the site repo9 Oct 2026
- cb463c8Menu without client code, one arrow id per card, VP in the profile; ignore resume source9 Oct 2026
- 61a1787Home: top menu, photo panel, project cards, resume button; richer case-study drawings9 Oct 2026
- f500879Add the resume PDF; QR attendance stack is JavaScript9 Oct 2026
4 · What the homepage weighs
- HTML
- 10.0 KB
- CSS
- 4.8 KB
- JavaScript
- 137.9 KB of a 150.0 KB budget
- Fonts (preloaded)
- 91.6 KB
- Total, gzipped
- 244.2 KB
the build fails if JavaScript goes over budget
5 · The Worker, annotated
01 The two endpoints
The page asks the Worker for the live numbers. The ping never counts as a visit, and if the counter breaks, the endpoint still answers so the page does not.
See the codeHide the code worker/handlers.ts · 31 lines
const NO_STORE = { 'cache-control': 'no-store' };
const json = (body: unknown, status = 200, headers: Record<string, string> = {}) =>
new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json', ...NO_STORE, ...headers } });
export const utcDay = (d: Date) => d.toISOString().slice(0, 10);
// The browser times this round trip. It never counts as a visit.
export function handlePing(req: Request): Response {
if (req.method !== 'HEAD' && req.method !== 'GET') return json({ error: 'method not allowed' }, 405, { allow: 'GET, HEAD' });
return new Response(null, { status: 204, headers: NO_STORE });
}
export async function handleVisit(
req: Request,
hit: (day: string) => Promise<number>,
build: { sha: string; time: string },
now = new Date(),
): Promise<Response> {
if (req.method !== 'GET') return json({ error: 'method not allowed' }, 405, { allow: 'GET' });
const colo = (req as Request & { cf?: { colo?: string } }).cf?.colo ?? null;
let visitsToday: number | null = null;
try {
visitsToday = await hit(utcDay(now));
} catch (err) {
console.error('visit counter failed', err); // the panel shows "–"; the rest still works
}
return json({ colo, visitsToday, build });
}
export const notFound = () => json({ error: 'not found' }, 404);02 Today's count
The count lives in a Durable Object, so two visits at the same moment never overwrite each other and no count is lost.
See the codeHide the code worker/visit-counter.ts · 12 lines
import { DurableObject } from 'cloudflare:workers';
import { nextCount, type DayCount } from './counter';
// One global instance. Durable Object storage is strongly consistent, so two
// visits at the same moment never overwrite each other's count.
export class VisitCounter extends DurableObject {
async hit(day: string): Promise<number> {
const next = nextCount(await this.ctx.storage.get<DayCount>('today'), day);
await this.ctx.storage.put('today', next);
return next.count;
}
}6 · Decisions
- Chose A static export over a Next.js server, because every page is the same for every visitor, so plain files are faster and have nothing to patch.
- Chose Cloudflare Workers over Pages, because one deploy serves the files and the two small APIs, and it is where Cloudflare is putting new features.
- Chose A Durable Object over KV, because KV is eventually consistent, so two visits at once could lose a count; a Durable Object cannot.
- Chose Plain CSS over a CSS framework, because the design has a handful of components, and tokens in custom properties cover them.
- Chose A native <details> toggle over a JS accordion, because the code is readable with JavaScript off and ships zero bytes of script.
Drawn byAryan Patel
Revision4.7
Buildca45814
Sheet2 / 2