Thanks for helping build Heliobond — a green-bond investment app on Stellar that opens green investing to everyone, from one dollar. The same values we promise users (transparency, plain language, no dark patterns, accessibility) apply to how we build: in the open, kindly, and to a high bar.
- Live demo: https://heliobond.vercel.app
- Architecture & layout: see
README.md - Code of conduct:
CODE_OF_CONDUCT.md
There's a lane for most skill sets — pick by what you enjoy:
| Lane | Examples |
|---|---|
| Frontend (React / TypeScript) | screens, components, the WebGL Helio, tests |
| Smart contracts (Rust / Soroban) | the registry + vault, wiring live reads/writes |
| Localization (no deep code needed) | translate the creator/admin/project surfaces, add a locale |
| Accessibility | WCAG audit passes, keyboard / screen-reader fixes |
| Design | extend the token system, motion, specimen cards |
| Docs | improve guides, examples, this file |
- Browse good first issues and help wanted, or this project on GrantFox.
- Claim it before you start — comment on the issue (or apply via GrantFox) so it's assigned to you and we avoid duplicate work. Every issue states its scope and acceptance criteria.
- No issue for your idea? Open a Feature request first so we can agree on scope before you build.
We don't merge unsolicited PRs that aren't tied to an accepted issue — it keeps the queue clean and your time well spent.
Prerequisites: bun 1.2.4 (the package manager / runner) and Node 18.18+.
git clone https://github.com/Heliobond/frontend.git
cd frontend
bun install
bun run dev # http://localhost:3000Useful scripts — run these before opening a PR:
bun run build # production build (must pass)
bun run typecheck # tsc --noEmit
bun run lint # ESLint
bun run format:check # Prettier — check only (used in CI)
bun run format # Prettier — rewrite files in place
bun run test # Vitest unit + component test suite
bun run test:e2e # Playwright end-to-end tests
bun run start # serve the production buildThe project uses Vitest with a jsdom environment and @testing-library/react for component rendering.
bun run test # run all tests once and exit
bun run test:ui # open the Vitest browser UIA shared render helper lives in src/test/render.tsx. It wraps components in
the i18n and theme providers the app uses, so component tests get a realistic
context. Import from there instead of @testing-library/react directly:
import { render, screen, fireEvent } from '@/test/render'Playwright drives a real Chromium browser against the running Next.js dev server.
bun run test:e2e # headless Chromium (starts dev server automatically)E2E tests live in e2e/. The deposit smoke test seeds a demo wallet via
localStorage so no real Stellar wallet extension is required.
- Branch off
main:git checkout -b <type>/<short-description>(e.g.feat/withdraw-max-chip,fix/helio-glow,i18n/creator-screens). - Make focused changes — one issue per PR.
- Run the checks locally:
bun run build(must pass),bun run typecheck,bun run lint,bun run format:check, andbun run test. - Open a PR using the template; link the issue with
Closes #123. - CI runs build, typecheck, lint, and format check on every PR;
mainis protected and requires green CI plus a maintainer review before merge.
CODEOWNERS requires maintainer review for sensitive areas — the wallet integration, design tokens, i18n catalogs, and CI.
Heliobond uses next-intl with cookie-based locale
selection. Message catalogs live at messages/en.json and messages/fr.json;
the request config in src/i18n/request.ts loads the matching catalog for the
current locale.
When you add or change user-facing copy:
- Pick the namespace that matches the surface using the copy, such as
Nav,Footer,Landing,Deposit, orProjectDetail. - Add the same key path to both
messages/en.jsonandmessages/fr.json. The catalogs must stay in parity: every namespace and key in English must also exist in French, and vice versa. - Translate the value in each catalog. Do not leave English placeholder text in
fr.jsonunless the issue explicitly calls for a temporary fallback. - Read the key from code with
useTranslations('<Namespace>'), then callt('<key>'). For example:
import { useTranslations } from 'next-intl'
export function Example() {
const t = useTranslations('Creator')
return <h1>{t('title')}</h1>
}To add a new namespace for a new screen or surface:
- Create the namespace object in both catalogs with identical keys:
{
"Creator": {
"title": "Build your project"
}
}- Add the translated French values under the same namespace and key names in
messages/fr.json. - Use that namespace from the component with
useTranslations('Creator'). - Run
bun run buildorbun run typecheckbefore opening the PR so missing or misspelled message keys are caught with the rest of the app checks.
- Builds and type-checks clean.
bun run buildis the gate; noanyto paper over types, no@ts-ignorewithout a comment. - Follow the design system. Use the CSS custom properties (
var(--ink),var(--solar), …) — never hardcode colours. Honour the brand rules: sentence case (no all-caps headlines), mono tabular numerals for figures, every delta carries a +/− sign and arrow (colour is never the sole carrier), solar is never text on a light background and never the only carrier of meaning, no emoji in the product, no exclamation marks on financial copy. SeeREADME.mdandsrc/styles/tokens/. - User-facing strings are translated. If you add or change copy in the shell or translated screens, add the key to both
messages/en.jsonandmessages/fr.json(they must stay in parity). - Accessibility is not optional. Keyboard operable, visible focus, semantic landmarks,
prefers-reduced-motionrespected, touch targets ≥ 44px. - No secrets in the repo or in client code.
- The issue's acceptance criteria are met.
- CI is green; the PR is reviewed and approved.
- UI changes include before/after screenshots (or a short screencast).
- Docs/translations updated where relevant.
- Bugs: open a Bug report issue with steps to reproduce.
- Security: please do not open a public issue. Use GitHub's "Report a vulnerability" (Security tab) for a private advisory.
The project ships a pre-commit hook via Husky + lint-staged that runs the TypeScript type-checker on staged files before each commit. Install it:
bun run prepareTo opt out, skip the prepare step — the hook is not installed unless you run it.
Contributors who opt out are still expected to run bun run build before opening a PR.
By contributing, you agree to abide by the Code of Conduct.