Skip to main content

Why These Rules Exist

folksbase uses strict rules to keep the codebase consistent and catch bugs early. Biome handles formatting and linting (replacing ESLint + Prettier), and TypeScript is configured with maximum strictness. CI rejects any violations — there’s no “fix it later.”

Biome

Biome handles both formatting and linting in a single tool. Configuration lives in biome.json at the repo root.
pnpm lint        # Check for issues
pnpm lint:fix    # Auto-fix everything
Biome ignores build outputs and files with unsupported syntax:
  • dist, .next, node_modules, migrations — build artifacts
  • storybook-static — Storybook build output
  • test-results, playwright-report — Playwright artifacts
  • **/globals.css — uses modern CSS features (@starting-style, @theme inline) that Biome can’t parse yet

TypeScript Strictness

Every tsconfig.json in the project has strict: true and noUncheckedIndexedAccess: true. This means:
  • All function parameters and return types must be explicitly typed
  • Array indexing returns T | undefined — you must narrow with ?. or ??
  • No any type, ever — use unknown and narrow, or create a proper type
  • No // @ts-ignore or // @ts-expect-error
// ❌ Will fail CI
async function getContact(id: any): Promise<any> { ... }
const name = rows[0].name;

// ✅ Correct
async function getContact(id: string): Promise<Contact> { ... }
const name = rows[0]?.name ?? "Unknown";

Import Rules

  • Use path aliases for cross-package imports: @folksbase/db, @folksbase/types, @folksbase/emails
  • Within an app, use @/* alias (e.g., @/lib/logger.js, @/env.js)
  • Backend imports must include .js extension for ESM compatibility
  • Never use relative paths crossing package boundaries (../../packages/db is forbidden)
// ❌ Forbidden
import { db } from "../../packages/db";
import { logger } from "../lib/logger";

// ✅ Required
import { db } from "@folksbase/db";
import { logger } from "@/lib/logger.js";

Export Rules

  • Named exports only — no export default except Next.js pages and layouts (framework requirement)
  • No barrel files (index.ts re-exporting everything) inside apps/
  • Import directly from the source file

Key Conventions

Types

  • Use type over interface unless you need declaration merging
  • Check @folksbase/types before creating new types — it may already exist
  • Zod schemas are the source of truth for runtime validation; derive TS types with z.infer<>

Error Handling

  • Never throw raw strings
  • API errors follow the { code, message, details? } shape from @folksbase/types
  • Let errors propagate to the global errorHandler middleware — don’t add manual try/catch in route handlers

Logging

  • No console.log in production code — ever
  • Use the structured logger: import { logger } from "@/lib/logger.js"
  • Methods: logger.info(), logger.warn(), logger.error() — all accept (message, extra?) where extra is Record<string, unknown>

Variables

  • Prefer const over let — restructure with ternary or early return when possible
  • Every let must have an explicit type annotation (Biome rule: noImplicitAnyLet)
  • Don’t shadow restricted names (Error, Object, Number, etc.)
  • Use async/await over .then() chains

CSS and Design Tokens

  • All colors use oklch-based CSS custom properties from globals.css
  • Use Tailwind token classes (text-gray-10, bg-green-a-3) — never hardcoded hex/hsl or Tailwind defaults like slate-*
  • Card containers use the .card class
  • Error states use token colors (border-red-a-7, text-red-a-9)
See the Frontend Architecture page for more on component patterns and styling.

Accessibility

All interactive UI components must include proper ARIA attributes:
ComponentRequired
Dialogaria-modal, aria-labelledby, aria-describedby
Dropdown menurole="menu" on container, role="menuitem" on items
Selectaria-haspopup="listbox", aria-expanded
Input (with error)aria-invalid="true", aria-describedby pointing to error element
Icon-only buttonsaria-label describing the action
Navigation linksaria-current="page" on active link
Table headersscope="col" on <th> elements
Error messagesrole="alert"

Quick Reference: Common Pitfalls

PitfallFix
Using anyUse unknown and narrow
console.logUse logger.info/warn/error
process.env.XImport env from @/env or @/env.js
Non-null assertion (!)Use ??, ?., or explicit null check
let without typeAdd explicit type or use const
Hardcoded colorsUse oklch design tokens
Missing ARIA attributesSee accessibility table above