How this site is built

Reejan designed and coded reejangopan.me himself — as a working example of how he builds production software: fast, accessible, search-friendly and maintainable.

  • 77 pages, every one prerendered to static HTML with its own title, description, canonical URL and structured data
  • React 18 + TypeScript + Vite, deployed on Vercel with clean URLs, immutable asset caching and security headers
  • Lighthouse 100 for desktop performance, SEO and best practices on the pages tested
  • Light and dark themes, phone-first layouts and keyboard-accessible navigation
  • A knowledge base of 55 guides and 100+ technology explanations behind the chat assistant, checked by an automated test suite
  • Inline critical CSS, preloaded fonts, and the chat code-split so it never slows the first load

Every page is server-rendered to static HTML

At build time each route is rendered with React's renderToString, its own <head> is injected, and the page is written in both URL styles so it resolves on any host. Google gets complete HTML — no blank page waiting for JavaScript.

prerender.js
for (const route of routes) {
  const html = template
    .replace("<!--app-head-->", head(route))   // title, meta, canonical, JSON-LD
    .replace("<!--app-html-->", render(route)) // React renderToString
  write("dist" + route + ".html", html)
  write("dist" + route + "/index.html", html)
}
writeSitemap(routes)                           // generated from the same list

Structured data as one connected graph

Each page publishes JSON-LD that links the website, the person, the employer and the page itself — ProfilePage, Service, TechArticle, BreadcrumbList — so search engines and AI assistants understand who built what.

src/seo/seo.ts
const graph = (...nodes: object[]) => [{
  "@context": "https://schema.org",
  "@graph": [website, person, employer, ...nodes],
}];

// e.g. a service page
graph(page("WebPage", path, title, description), service, breadcrumbs(trail))

A search engine for the chatbot, written from scratch

Devv, the site's assistant, ranks answers with BM25 — the classic search-engine scoring function — plus synonym expansion, stemming and Damerau-Levenshtein typo correction. It runs entirely in the browser: free, instant and private.

src/chat/engine.ts
for (const t of queryTerms) {
  const f = termFreq[doc].get(t);
  if (!f) continue;
  const idf = Math.log(1 + (N - df(t) + 0.5) / (df(t) + 0.5));
  score += idf * (f * (k1 + 1)) /
    (f + k1 * (1 - b + (b * docLen[doc]) / avgLen));
}
// answer only when confident; otherwise suggest related questions

Scroll animations that work in every browser

Reveal animations are driven by IntersectionObserver and animate only transform and opacity, so they run on the GPU. Content is never hidden without JavaScript, and everything respects reduced-motion settings.

src/components/site/fx.ts
const io = new IntersectionObserver((entries) => {
  for (const e of entries) {
    if (!e.isIntersecting) continue;
    reveal(e.target);          // adds .is-in — CSS does the rest
    io.unobserve(e.target);
  }
}, { rootMargin: "0px 0px -8% 0px" });

Want this quality for your business?

The same approach powers his client work — see the case studies, the services and the tech guides.