Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.folksbase.joselito.dev/llms.txt

Use this file to discover all available pages before exploring further.

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