This is the Scaler Open Source Club's own website, and it is also meant to be a first open-source contribution for club members. If this is your first pull request anywhere, you are the intended audience — say so in the PR and someone will walk you through it.
git clone https://github.com/<org>/<repo>.git
cd <repo>/web
npm install
npm run dev # http://localhost:3000
# port 3000 is often taken; use `npm run dev -- -p 3001`
# and pass SITE_URL=http://localhost:3001 to the checksNo credentials, no database, no .env. If npm run dev needs anything from you
beyond those commands, that is a bug — please open an issue.
Almost every contribution is an edit to one file under web/content/. They hold
all the site's content as typed arrays, one module per page, so you do not need to
touch a React component to add a person, a project, or an answer.
| File | What lives there | Page |
|---|---|---|
site.ts |
Links, the page list, the footer's institutional copy | every page |
essence.ts |
What open source is, career impact, the ICPC comparison, member stories | Essence |
projects.ts |
Build-day projects, club repos, upstream contributions | Projects |
programs.ts |
The seven programmes, tiers, the reverse clock | Programs |
people.ts |
Core team, alumni, achievers, organisations | Hall of Fame |
join.ts |
The four entry paths, what we look for, culture, FAQ, form options | How to Join |
This used to be a single club.ts. It was split when the site became five pages,
because one 800-line file holding five pages' content meant every content PR touched
it and every one of them conflicted.
Editing the join form's options?
join.tsholds the four paths, the two hostels and the programme list — andfirestore.rulesat the repo root keeps a second copy of those values, because Firestore rules cannot import anything. Change one without the other and every member who picks the new option gets a permission error on save, while the page still renders perfectly. Runnpm run rulesto check, and see FIREBASE.md.The signed-in half of the site —
/join,/onboarding,/dashboard,/admin— is not content and does not live inweb/content/. Nothing there is published on the site; see FIREBASE.md before changing it.
// content/people.ts
export const ACHIEVERS: Achiever[] = [
{
name: "Full Name",
achievement: { kind: "programme", programme: "GSOC" }, // or { kind: "hackathon", event: "..." }
year: "2026",
org: "The organisation that selected them",
work: "One specific sentence on what they actually built.",
photo: "/people/full-name.jpg", // optional
github: "their-login", // optional
url: "https://link-that-proves-it",
consented: true, // REQUIRED — see below
},
];Run npm run typecheck. If it passes, the shape is right.
Several sections render obvious placeholders — "Placeholder One", "Example
Foundation" — in development and nothing at all in production. That is
deliberate: a card grid cannot be designed or reviewed against an empty array, but a
production build must never ship an invented name. The switch is
process.env.NODE_ENV, so it is structural rather than a note asking somebody to
remember.
When you add real entries, the placeholders disappear on their own — the
published*() helpers return real content whenever there is any. Do not delete the
scaffolds; the next person redesigning that section needs them.
Every person entry carries consented. Anything not explicitly true is filtered
out and never rendered.
Do not set consented: true on someone else's behalf because you think they would
not mind. These are named students, with photos, shown to an international
audience — including people who may not want their name attached to a specific
organisation publicly. Ask them, then set the flag.
The site has no testimonials and no placement statistics. External verifiability is the only thing making it credible, and the whole page is built on "you can check this". So:
- A number needs a link to the page that states that number.
- The linked page must state the same number. Citing 1,272 while linking a source that says 1,280 is the exact failure this rule exists to prevent — that one shipped once and had to be corrected.
- If you cannot find a primary source, the claim does not go in. This has already cost two good-sounding lines: an invented "only 9 people per college make ICPC" figure that no rulebook or dataset produces, and "open source is an easier door than ICPC", which is false — GSoC 2025 accepted 1,280 of 15,240 applicants.
A weaker claim you can prove beats a stronger one you cannot.
Match the voice already there. Concretely:
- Say what happened, not how it felt. "Merged a parser fix into OpenCRE" beats "made an amazing contribution".
- No adjectives describing people. Attach them to work instead.
- Sentence case. No exclamation marks.
- Prefer the specific. "Three terms a year" beats "runs frequently".
npm run typecheck # must pass
npm run palette # must pass — categorical colour constraints
npm run build # must pass
npm run smoke # needs `npm run dev` in another terminal
npm run qa # must report 0 issues — same
npm run browsers # all three enginesnpm run qa drives real Chromium across every route × four viewports × both
themes — 80 combinations. It checks contrast, tap-target sizes, text size, heading
order, alt text, horizontal overflow, and that .tap is never combined with a margin
utility. Zero issues is the bar, and it is not negotiable for a site whose
audience includes people reading it on a phone on campus wifi.
npm run smoke is the one that catches a dead bundle: it asserts hydration, the Join
button's visibility on every route, and that the outline and scroll reveals re-derive
after a client-side navigation. Run it before qa, because a page serving no
JavaScript passes most of what qa checks.
Both of those run signed out, so on /onboarding and /dashboard they only ever see
the "sign in first" card. If you touched sign-in, the profile form, the dashboard or
anything under /admin, a green run there means the door is not broken and nothing more —
the signed-in half is covered by npm run e2e:auth, which drives the whole flow in a real
browser against the Auth and Firestore emulators, and by npm run rules:emulator, which
executes firestore.rules as several different people. Both need the emulators running;
see FIREBASE.md.
If you changed anything visual, also look at it in both themes. On this project that has caught bugs every single time — including a wordmark rendering at 1.11:1 (black on black) that every automated checker passed, because the checker read the declared token and the element was rendering transparent.
Do not run
npm run buildwhilenpm run devis running. They share.next, so the build deletes the chunks the dev server is serving. The page still returns 200 and looks fine, but no JavaScript loads. Recover withrm -rf .nextand a restart;node scripts/smoke.mjsconfirms.
- One change per PR. A content addition and a layout refactor are two PRs.
- Say what you changed and why. If it is visual, attach a before/after screenshot in both themes.
- Link the issue if there is one.
- Do not commit
study/output,node_modules,.next, or any.envfile. All are gitignored already; if something slipped through, that is worth an issue.
Look for the good first issue label. Genuinely useful starting points:
- Add an achiever, a project or a core team member to the right file in
web/content/(with consent + proof). - Add an FAQ entry for a question you actually had before joining.
- Fix a
npm run qafailure on a viewport we have not looked at closely. - Improve a section's copy to be more specific and less promotional.
Be straightforward and useful. Review the code, not the person. Nobody here is expected to already know how open source works — that is the entire point of the club.