> ## 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.

# Code Style

> Biome rules, TypeScript strictness, and coding conventions enforced across the project.

## 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.

```bash theme={"dark"}
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`

```typescript theme={"dark"}
// ❌ 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)

```typescript theme={"dark"}
// ❌ 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](/architecture/frontend) page for more on component patterns and styling.

## Accessibility

All interactive UI components must include proper ARIA attributes:

| Component          | Required                                                            |
| ------------------ | ------------------------------------------------------------------- |
| Dialog             | `aria-modal`, `aria-labelledby`, `aria-describedby`                 |
| Dropdown menu      | `role="menu"` on container, `role="menuitem"` on items              |
| Select             | `aria-haspopup="listbox"`, `aria-expanded`                          |
| Input (with error) | `aria-invalid="true"`, `aria-describedby` pointing to error element |
| Icon-only buttons  | `aria-label` describing the action                                  |
| Navigation links   | `aria-current="page"` on active link                                |
| Table headers      | `scope="col"` on `<th>` elements                                    |
| Error messages     | `role="alert"`                                                      |

## Quick Reference: Common Pitfalls

| Pitfall                  | Fix                                     |
| ------------------------ | --------------------------------------- |
| Using `any`              | Use `unknown` and narrow                |
| `console.log`            | Use `logger.info/warn/error`            |
| `process.env.X`          | Import `env` from `@/env` or `@/env.js` |
| Non-null assertion (`!`) | Use `??`, `?.`, or explicit null check  |
| `let` without type       | Add explicit type or use `const`        |
| Hardcoded colors         | Use oklch design tokens                 |
| Missing ARIA attributes  | See accessibility table above           |
