Skip to content

Latest commit

 

History

History
200 lines (156 loc) · 9.94 KB

File metadata and controls

200 lines (156 loc) · 9.94 KB

CLAUDE.md

Orientation for AI coding assistants (and new contributors) working in this repo. Read this first, then reach for README.md when you need full reference detail (deployment recipes, integration URLs, complete design-system tables).


What this is

The official website for the 256 Foundation (256foundation.org) — a 501(c)(3) nonprofit funding the open-source Bitcoin mining ecosystem. It's a content-driven marketing/informational site: inform (mission, projects, ecosystem), fund (BTC/fiat + hashrate donations), and recruit (grant applications). No database, no CMS, no auth — all content lives in TypeScript files under data/ and MDX under content/.

  • Repo: git@github.com:256foundation/website.git (org: 256foundation)
  • Hosting: Vercel (production deploys on push to main; PRs get preview URLs)

Commands

npm install       # install deps (Node 20 LTS+ required — uses AbortSignal.timeout)
npm run dev       # dev server → http://localhost:3000
npm run build     # production build (prerenders 19 routes; makes live network calls)
npm start         # serve the production build
npm run lint      # ESLint (next lint)
npm test          # Node built-in test runner over tests/*.test.mjs

There is no separate typecheck script; npm run build runs tsc as part of the Next build. strict mode is on (tsconfig.json). The @/* path alias maps to the repo root.


Tech stack

Next.js 15 (App Router, React 19, RSC-first) · TypeScript (strict) · Tailwind CSS v4 · next-mdx-remote + gray-matter (newsroom) · fast-xml-parser (Substack feed). That is the entire runtime dependency set — no UI kit, CSS-in-JS, state library, ORM, or 3D engine. Check package.json before assuming a library is available.

Tailwind v4 is configured CSS-first in app/globals.css via @theme and @custom-variant — there is no tailwind.config.js. PostCSS config is postcss.config.mjs.


Rendering model

Static-first with hourly ISR for pages that show live external data. Verified route map (from next build):

Route Render Notes
/ Static + ISR (revalidate = 3600) fetches Substack, forum, GitHub org events
/mission, /donate, /grants, /faq, /telehash Static pure data-file content
/newsroom force-static lists MDX posts
/newsroom/[slug] SSG (generateStaticParams) MDX detail; force-static
/projects Static + ISR (revalidate = 3600) grant log + live org repos/stats
/projects/[slug] SSG + ISR (revalidate = 3600) 4 slugs: ember-one, mujina, libre-board, hydrapool
/api/hashdash Dynamic (force-dynamic) the only API route
/robots.txt, /sitemap.xml Static generated by app/robots.ts, app/sitemap.ts

Root layout (app/layout.tsx) wraps everything with HeaderAnnouncementBanner → page → Footer, loads the three Google fonts, sets base Metadata, and conditionally injects the Umami script (production + both Umami env vars).


Layout of the code

app/            App Router pages (see route map above) + globals.css, robots.ts, sitemap.ts
components/
  ui/           primitives (Button, Card, Badge, SectionWrapper, PCBBackground, …)
  layout/       Header, Footer, MobileNav, NavDropdown, AnnouncementBanner, TelehashExtensionDetector
  home/         home-page sections (Hero, DonateCards, StayUpdated, HashrateLeaderboard, ContactForm, …)
  projects/     project-detail sections (PillarProjectCard, MilestoneTracker, GrantLogTable, ProjectForumSection, GitHubActivitySection, …)
  newsroom/     PostCard, PostBody (MDXRemote wrapper)
  shared/       TeamMemberCard, Timeline, NewsletterSignup, SubstackEmbed
  telehash/     CountdownTimer, LeaderboardTable, PhotoCarousel, TeleHashEventCard
data/           ★ site content as typed TS — edit here, not in components
content/newsroom/*.mdx   newsroom articles (frontmatter + markdown body)
lib/            server-side data fetchers (see below)
types/          all shared interfaces (types/index.ts)
public/         images: logos/ projects/ team/ supporters/{tier1,2,3}/ ecosystem/ newsroom/<slug>/ telehash/ og/
tests/          node --test suites

Data layer (how to change content)

Almost all content edits happen in data/ — no component changes needed. Types for every structure are in types/index.ts.

File Controls
data/navigation.ts header nav + dropdowns (topNav) and footer links
data/projects.ts the 4 pillar projects (pillarProjects): specs, milestones, team, GitHub + forum URLs
data/grants.ts grant recipient log (grantLog)
data/supporters.ts supporter logos by tier (supporters)
data/team.ts founders + board
data/stats.ts siteStats (BTC raised, blocks found, grantees)
data/telehash.ts teleHashEvents, nextEventDate/nextEventDetails (countdown)
data/faq.ts faqItems (categorized Q&A)
data/donate.ts on-chain BITCOIN_ADDRESS + LIGHTNING_ADDRESS
data/announcements.ts activeAnnouncement (banner) — null hides the banner

Newsroom posts: add content/newsroom/<slug>.mdx with frontmatter (title, date, author, category, excerpt, optional coverImage/ogImage). Parsed by lib/newsroom.ts; sorted newest-first; the newest post auto-surfaces on the home page. Article images go in public/newsroom/<slug>/.


External integrations (lib/)

All fetchers fail soft — on any error they return []/null/fallback so the page still renders. GitHub/forum/Substack fetches use next: { revalidate: 3600 }.

  • lib/github.ts — GitHub REST: repo meta (stars/forks/issues/latest release), org repos, org stats, org events, recent commits. Adds Authorization: Bearer only if GITHUB_TOKEN is set.
  • lib/discourse.ts — Discourse forum (forum.256foundation.org): latest topics + per-project category topics; plus forumTopicUrl / timeAgo helpers.
  • lib/substack.ts — parses the Substack RSS feed for post cards.
  • lib/metadata.tsgeneratePageMetadata(); every page uses it for OG/Twitter/canonical tags.
  • app/api/hashdash/route.ts — live hashrate leaderboard: 3 parallel Prometheus queries against pool.256foundation.org/api/v1/query, resolves npub… identities to Nostr names/avatars via Primal (10-min in-memory cache), 5s per-call timeout, 503 on upstream failure.

Contact form (components/home/ContactForm.tsx) POSTs directly to Formspree (formspree.io/f/xkndjepy) client-side — no server route.


Design system (follow these — the site has a deliberate, consistent look)

Defined as tokens in app/globals.css. Full tables are in the README; the rules that matter when writing UI:

  1. No border radius — everything is rounded-none (square corners). Intentional.
  2. Purple is the only accent#3b1445 (light) / #c084d8 (dark). No orange.
  3. Terminal green #00FF41 = live/success only — status dots, live indicators.
  4. Depth via borders, not shadows#1f1f1f borders in dark mode.
  5. Uppercase display headings — Barlow Condensed (font-display); Space Mono (font-mono) for labels/buttons/nav/stats; Inter (font-sans) for body.
  6. Dark mode is the primary experience, driven by @media (prefers-color-scheme)not a toggle. The dark: variant is remapped to that media query via @custom-variant, so there is no .dark class and no theme switcher. Style both modes.
  7. Use (&rarr;) for directional UI, not chevrons.

Environment variables

All optional — the site builds and runs with none (graceful fallbacks). Only these are actually read by code: NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_ZAPRITE_URL, NEXT_PUBLIC_UMAMI_WEBSITE_ID, NEXT_PUBLIC_UMAMI_URL, GITHUB_TOKEN, plus Vercel's auto VERCEL_URL. See .env.example, which also lists removed vars to avoid (Resend, Hashdash, Typeform) so they don't creep back in.


Gotchas & non-obvious facts

  • ARCHITECTURE.md and SPEC.md are stale/aspirational. They describe a Three.js + GSAP 3D scroll experience and a Resend /api/contact route that were never shipped. Both files now carry a warning banner at the top. Trust this file, the README, and the code — not those two for current implementation detail. (The orphaned HardwareProjectViewer/HashStreamCanvas placeholder components and the empty app/api/contact/ directory those docs referenced have since been removed.)
  • Discourse slug typo: Libre Board's forum category is fibre-board (legacy), not libre-board. See forumCategoryApiUrl in data/projects.ts — don't "fix" it.
  • TeleHash browser-extension coexistence: a Chrome extension injects a fixed 115px topbar at a huge z-index. components/layout/TelehashExtensionDetector.tsx + the --ext-offset CSS var + .site-main/header styles in globals.css handle this. Be careful editing fixed-position header/offset styling.
  • Grant cycle is currently closed — the /grants apply CTA and NEXT_PUBLIC_TYPEFORM_URL are intentionally removed; re-add when a cycle reopens.
  • Mining pool address pool.256foundation.org appears as hardcoded strings in app/donate/page.tsx and app/telehash/page.tsx (not in a data file).

Related docs

  • README.md — full reference: deployment (Vercel / Proxmox / Coolify / Netlify / Railway), integration URLs, complete design-system tables, content-update recipes.
  • ARCHITECTURE.md / SPEC.md — original design intent (see stale-warning banners at their tops).
  • discourse-header-prompt.md — one-off prompt for matching the Discourse forum header to the site header; not part of the app build.