# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build/Test Commands ```bash npm run dev # Start dev server npm run build # Production build (static site via adapter-static) npm run test # Run all tests once npm run test:unit # Run tests in watch mode npx vitest run src/lib/ling/test/ling.test.ts # Run single test file npx vitest run -t "describe name" # Run tests matching pattern npm run check # Type check with svelte-check npm run lint # Check formatting with Prettier npm run format # Auto-format with Prettier ``` ## Code Style - Tabs for indentation, single quotes, no trailing commas, 100 char print width (Prettier) - Spanish comments in code are acceptable - Svelte 5 runes mode enforced globally (`svelte.config.js` `dynamicCompileOptions`) - DOM event handlers: lowercase (`onclick`, `onfocus`). User callbacks: camelCase (`onComplete`, `onInteractOutside`) - State fields: `$state()` without `#`. Derived: `readonly x = $derived.by(...)` - Boolean attrs: always use helpers (`boolToTrueOrUndef`, `boolToEmptyStrOrUndef`, `boolToStr`) — never ternaries ## Path Aliases Aliases must be synced in BOTH `svelte.config.js` AND `vite.config.ts`: | Alias | Target | | ----------------------- | ------------------------------------------------------ | | `@/` | `src/` | | `@/ling` | `src/lib/ling` (i18n) | | `@/logr` | `src/lib/logr` (logging) | | `@/glob` | `src/lib/glob` (legacy; do not use for new code) | | `@/actx` | `src/lib/actx` (app context) | | `@/uiux` | `src/lib/uiux` | | `$uix` | `src/uix` (UI layers) | | `$terra` | `src/uix/terra` (headless primitives) | | `$terra/utils` | `src/uix/terra/utils/sound.svelte.ts` | | `$terra/system` | `src/uix/terra/system/exports.ts` | | `$terra/external/dates` | `src/uix/terra/external/dates/sound.svelte.ts` | | `$lib` | `src/lib` | | `$lib/util/dias` | `src/lib/util/dias` (date/time library — canonical) | ## Vitest Two-Project Structure `vite.config.ts` defines two test projects: - **client**: Browser tests via Playwright for `*.svelte.{test,spec}.{js,ts}` - **server**: Node environment for `*.{test,spec}.{js,ts}` (excludes svelte tests) ## Architecture: Three-Layer UI System ``` terra → headless primitives: behavior, accessibility, data-* contracts, context air → visual layer on top of terra: tokens, themes, recipes, styled components glob → domain logic: dates, numbers, currency, units, locale ``` The dependency flows one way: `air` → `terra`. Terra never imports from air. ### terra (`src/uix/terra/`) Headless primitive library. Each component follows `Element.SubElement` pattern (`Dialog.Root`, `Dialog.Trigger`, `Dialog.Content`). **Key patterns:** - Thin `.svelte` wrappers + logic in `*.svelte.ts` State classes - State classes use `static create()` for context registration, `createComposed()` for internal composition without context - Reactive system: `Active`, `State`, `readableActive()`, `writableActive()` — values travel as boxes with `.current` - Context via `createTerraContext()` (wraps runed's `Context`) - Props merging: `mergeProps(restProps, state.props)` in every wrapper - `data-*` attrs are formal public API, validated via `assertDataContract()` against `utils/contracts.ts` - `child` snippet replaces the rendered node; `children` fills the interior **Structure:** - `[component]/components/` — Svelte wrappers - `[component]/types.ts` — public props - `[component]/*.svelte.ts` — State classes - `[component]/exports.ts` + `sound.svelte.ts` — barrel exports - `layers/` — shared infra (portal, floating, presence, focus-scope, dismissible, escape, scroll) - `dom/` — browser-dependent utils (focus, roving focus, arrow navigation) - `utils/` — pure helpers (state system, attrs, merge-props, events, keyboard, css, datetime) - `system/` — `TerraConfig` for cross-cutting services (translator, formatters, logger, dir, portalTo) - `external/dates/` — adapter boundary for date library **Import rules within terra:** use `$terra/utils` or relative paths internally, never `$uix/terra`. ### air (`src/uix/air/`) Visual layer built on terra. Consumes terra's public anatomy and `data-*` attrs — never internal classes or incidental DOM structure. **Three-level token system:** 1. **Primitive tokens** (`tokens/primitive.css`) — absolute scale values, never used directly by components 2. **Semantic tokens** (`tokens/semantic.css`) — theme-aware, switch via `[data-theme]` 3. **Component tokens** (`tokens/components/*.css`) — scoped per component (`--air-button-*`), the only level component CSS writes **Token naming:** - Public: `--air-{component}-*` (stable, overridable) - Private: `--_air-{component}-*` (implementation detail, may change) **Contracts** (`contracts/`): - `contract.css` — minimum semantic tokens any theme must implement - `contract-categorical.css` — optional categorical color families - `contract-primitives.css` — optional advanced 12-step scales - `contracts/components/*.css` — per-component public token API **CSS rules:** - Tokens and `data-*` selectors must be global CSS (imported `.css` files), not `