The website is a standalone frontend. Most contributions can be done against production ScoreSaber services without running the full platform stack
For local setup, start with SETUP.md
Use Vite+ for package work. It manages the pinned pnpm version for the project:
vp install
vp add <package>
vp remove <package>Direct pnpm also works without a global Vite+ install. Use the version pinned in package.json, run pnpm install, then invoke built-in Vite+ commands through pnpm exec vp. Do not use npm, Bun or yarn to install dependencies
- kebab-case for TypeScript filenames and directories
- Use named exports
- Omit explicit TypeScript return types when inference is clear
- Keep explicit return types when they make a public contract clearer or inference is weak
- Use runtime validation instead of casts when dealing with unknown strings or request data
- Use the existing shadcn/ui components in
src/components/uibefore creating raw controls - Add
cursor-pointerto interactive shadcn controls where the primitive does not already provide it - Use semantic design tokens and helpers instead of hardcoded colors
- Put user facing text in
messagesand read it withuseTranslationsorgetTranslations
- Routes live in
src/routesand exportRoutefrom the route file. Add route params and search validation in the route config with Zod - Use route links, generated route APIs, or existing URL helpers for internal navigation. Do not manually assemble URLs when typed route helpers are available
- Fetch route page data from route loaders and server functions with the server API client
- Client components use TanStack Query for async API work
- Do NOT use raw
useEffectplusfetchfor client API state - Use shared API wrappers instead of raw
try/catch:pageApiDatafor route loaders that render inline page errorsoptionalApioroptionalApiDatawhen missing or failed data can be treated asnullrequiredApiDatawhen a failed required resource should throwqueryApiDatain TanStack Query functions that should reject on API errorsapiResultwhen the caller needs to branch on the full resultactionApiData,actionApiVoid,actionResult, oractionResultVoidinside server actions
- No raw
try/catchunless absolutely necessary. Use better-result helpers for fallible work - Server actions return
ActionResult. UseunwrapActioninside mutation functions andresult.okchecks outside mutations
src/shared/api/generated/ApiParams.ts is the source of truth for generated API types
Do not manually edit generated client files. After API contract changes, regenerate the client:
vp run api:generateUse vp run api:regen only when the local API is running and you need to fetch a fresh OpenAPI spec
Run the full check before committing:
vp run verifyOur commit style is {feature}: {change_summary} (#{issue_number}) (sometimes maintainers are naughty and bypass the need for an issue number, do not be like the maintainers)
Example:
rank-request: fix comment wrapping (#55)
denyah: destroy the page some more (#1)