A full redesign of the statically exported Next.js catalog under site/, plus the deployment metadata a public domain needs. Experience tier (/, /skills, /domains, 404): the library's cross-reference graph drawn on a Canvas 2D ring from a build-time asset (public/graph.json, checked against the library at build time), an editorial "code that reads as correct" spread, a pinned seven-section anatomy, a per-skill length spectrum, a marquee of real handoffs, smooth scroll and a contextual cursor loaded after hydration. Document tier (/skills/[slug], /domains/[slug]): the Markdown pipeline wraps each level-2 section so the seven guaranteed sections are numbered structure; "When NOT to Use" is styled as the boundary with handoff arrows; a contents rail with fixed positions; handoff lists in both directions; code frames with a delegated copy control. No animation library, no smooth scroll, CSS-only entrances. System: one token set for both themes, sixteen domain hues through the .tint mechanism, Geist / Geist Mono / Instrument Serif italic, a motion scale of two curves and four durations. Entrances are CSS scroll-driven animations; motion (the library) is removed. Hover-only link prefetch. A Star pill in the header with the live GitHub star count. Deployment: canonical and OpenGraph URLs, sitemap.xml and robots.txt driven by NEXT_PUBLIC_SITE_URL (default https://skills.himanshujangir.com); README documents the Vercel project settings. CLAUDE.md no longer references the removed GitHub Pages workflow. Measured (gzip, Next report): landing 123 kB First Load JS, document pages 109 kB, shared 103 kB; 501 skill pages, 16 domain pages, 520-URL sitemap. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.4 KiB
Catalog site
A statically exported Next.js browse-and-search interface for the skill library. It is a
view of the repository, not a second copy of it: every page is generated at build time
from index.json and the SKILL.md files under skills/, so a skill added, renamed or
removed shows up on the site with no edit here.
cd site
npm install
npm run dev # http://localhost:3000
npm run build # static export into site/out
npm run typecheck
The build needs the repository checked out around it — it reads ../index.json and
../skills/**/SKILL.md — so it cannot be built from site/ alone.
What is generated and what is authored
| Authored by hand | Generated at build time |
|---|---|
lib/domains.ts — label, one-line summary and accent hue for each of the 16 subdomains |
Every skill page, its prose, numbered sections, contents rail and file listing |
| Section copy on the landing page and the seven-section anatomy notes | All counts, word totals, the domain totals, the cross-reference graph |
| The four failure examples on the landing page (slugs are verified against the library) | The search index, the graph asset, the catalog, the domain pages |
lib/domains.ts is the only file that has to change when the library gains a domain, and
the build fails loudly if index.json names a subdomain it does not know.
Two tiers, one system
The site has two kinds of surface that share type, palette and spacing but not intensity:
- Experience tier —
/,/domains,/skillsand the 404. The canvas graph of the whole library, one pinned scroll sequence, smooth scroll (Lenis) and a contextual cursor. There is no animation library: entrances are CSS scroll-driven animations (.reveal,.word,.strikein globals.css), the pinned section is one scroll listener (lib/use-section-progress.ts). The canvas and Lenis load after hydration throughnext/dynamic, in their own chunks. - Document tier — the 501
/skills/[slug]pages and the 16/domains/[slug]pages. Reference material: a reading column, seven numbered sections, a contents rail, CSS-only entrances that finish within 400 ms. No motion library, no Lenis, no canvas. The build reports these routes at roughly 110 kB First Load JS against a 180 kB budget.
prefers-reduced-motion is a designed state on both tiers: the pinned sequences render as
static grids, the graph draws a still frame, Lenis and the cursor never initialise.
Design tokens
app/globals.css holds the whole system. Colour is rationed: the chrome is monochrome in
both themes, the sixteen domain hues are the only colour, and they arrive through one
mechanism — the .tint class turns a --hue custom property into --tint, --tint-wash
and --tint-line, resolved per theme. lib/domains.ts exports the same formula as
tintFor() for anything drawn with JavaScript. Motion tokens (two curves, four durations,
one stagger rule) live in the same file and in lib/motion.ts.
How the build enforces freshness
lib/content.ts throws rather than rendering something inconsistent when:
index.jsondisagrees with its owntotal_skills,- an entry points at a
SKILL.mdthat does not exist, - a subdomain has no entry in
lib/domains.ts, - the landing page names a skill slug that is no longer in the library.
CI adds one more check after the export: the number of directories under out/skills/ must
equal total_skills.
Search and the graph asset
Search is client-side over public/search-index.json, a ~260 KB projection of index.json
regenerated by scripts/generate-search-index.mjs before every build and dev run.
scripts/generate-graph.mjs emits public/graph.json the same way: 501 nodes and every
cross-reference between skills, ~56 KB, fetched only by the pages that draw it. The landing
page checks its node and edge counts against the library at build time. It is
fetched once per visit and shared by the catalog and the Cmd/Ctrl+K palette. Inlining it
into 500+ exported pages instead would have made the site larger than the library.
Matching is token-prefix over weighted fields (name, tags, brokers, subdomain, description
position), deliberately not fuzzy — every description in the library opens with
Use when …, so queries are situations whose words appear literally in the text.
Deploying
The export is plain static files in out/. The production deployment is Vercel:
- Root Directory
site, framework preset Next.js (it detectsoutput: "export"and servesout/), build commandnpm run build, Node 22. - Enable "Include source files outside of the Root Directory in the Build Step" in the
project's Git settings: the build reads
../index.jsonand../skills/**/SKILL.md. - Leave
NEXT_PUBLIC_BASE_PATHunset. SetNEXT_PUBLIC_SITE_URLto the public origin (defaulthttps://skills.himanshujangir.com); it drives canonical links, OpenGraph URLs,sitemap.xmlandrobots.txt. - Optional ignored-build step, so a commit that touches only the Python side does not
redeploy:
git diff --quiet HEAD^ HEAD -- . ../skills ../index.json ../README.md.
Any other static host works the same way: build with the two variables above and serve
out/. For a GitHub Pages project site, set NEXT_PUBLIC_BASE_PATH=/<repo> instead.
Not in scope
This is the browse layer only. Running a skill's helper or its unittest suite in the browser is a separate, larger piece of work; nothing here depends on it or blocks it.