You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
7.1 KiB
153 lines
7.1 KiB
# 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` (globalization: dates, numbers, locale) |
|
|
| `@/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/config` | `src/uix/terra/config/exports.ts` |
|
|
| `$terra/external/dates` | `src/uix/terra/external/dates/sound.svelte.ts` |
|
|
| `$glob` | `src/lib/glob` |
|
|
| `$lib` | `src/lib` |
|
|
|
|
## 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<T>`, `State<T>`, `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)
|
|
- `config/` — `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 `<style scoped>`
|
|
- Svelte scoped styles only for properties that don't depend on `data-*` or terra internals
|
|
- Components respond to terra states purely via `data-*` selectors: `[data-disabled]`, `[data-state='open']`, etc.
|
|
- Theme activation via `data-theme` attribute on any DOM container (supports nesting)
|
|
- Variant dimensions are orthogonal: `size` → dimensions, `variant` → appearance, `state` → transversal overrides
|
|
|
|
**Structure:**
|
|
- `components/` — styled wrappers over terra primitives
|
|
- `tokens/` — token implementation (primitive, semantic, per-component)
|
|
- `contracts/` — public token interfaces for theme authors
|
|
- `themes/` — theme implementations (base light/dark)
|
|
- `icons/` — icon system (`Icon` + per-icon components in `icons/lib/`)
|
|
|
|
### Internal Libraries (`src/lib/`)
|
|
|
|
- `ling` — i18n system (factory: `createLing`)
|
|
- `logr` — logging (factory: `createLogr`, depends on `ling`)
|
|
- `glob` — globalization: dates, numbers, currency, locale
|
|
- `actx` — app context
|
|
- `vice` — app-level utilities
|
|
|
|
**Important:** `DateValue` and all date utilities come from `@/glob/lib`, never from `@internationalized/date` directly. Within terra, use `$terra/external/dates`.
|
|
|
|
### Test Pages
|
|
|
|
Component demos live in `src/routes/test/`. Each terra/air component should have a test page demonstrating its states and variants.
|
|
|
|
## TerraConfig Integration
|
|
|
|
```svelte
|
|
<TerraConfig {translator} {dateTimeFormatter}>
|
|
<Dialog.Root />
|
|
</TerraConfig>
|
|
```
|
|
|
|
Priority chain for resolved props: explicit component prop → TerraConfig value → internal fallback. TerraConfig supports nesting (child overrides parent partially).
|
|
|
|
## Key Conventions
|
|
|
|
- When creating terra components: follow the checklist in `src/uix/terra/README.md` §21
|
|
- When reading existing modules: start with `exports.ts` → `types.ts` → root `.svelte` → `*.svelte.ts` → child wrappers → test page
|
|
- `$bindable()` without fallback when parent might pass `undefined`; apply defaults via coalescing (see `SVELTE5_BINDABLE_PATTERN.md`)
|
|
- Components that use Portal: consumer must manage `z-index` explicitly (layers don't impose z-indices)
|