This file contains guidelines for agentic coding agents working on this package. Refer to ../AGENTS.md for general guidelines.
- TypeScript + Lit web components (with Preact for internal components)
- Build: Vite
- Testing: Vitest, Playwright, Storybook
- Styling: TailwindCSS 4 + DaisyUI
- Maps: chartjs, Leaflet
- Utilities: Dayjs, Zod
- Package Manager: npm
npm ci # Install dependenciesnpm run build # Full build (dist + standalone bundle)npm run lint # Run lit-analyzer + eslint
npm run lint:eslint # ESLint only
npm run lint:eslint -- --fix # ESLint only and autofix problems where possible
npm run lint:lit-analyzer # Lit analyzer only
npm run check-types # TypeScript type checking (no emit)
npm run check-format # Prettier check
npm run format # Format code with Prettier# Generate custom elements manifest (required before starting Storybook)
npm run generate-manifest
# Start Lit Storybook (public documentation, port 6006)
npm run storybook
# Start Preact Storybook (development & testing, port 6007)
npm run storybook-preact
# Watch mode for manifest generation
npm run generate-manifest:watch# Unit tests
npm run test
# Storybook interaction tests (requires Storybook to be running)
npm run test:storybook # Lit Storybook
npm run test:storybook:preact # Preact Storybook
# Playwright tests (visual regression, CSV snapshots)
npm run test:playwright
npm run test:playwright:update-snapshots # Update snapshotsThe codebase uses a unique three-layer architecture that combines Preact and Lit:
-
Preact Components (
/src/preact/) - Internal implementation layer- Contains all business logic, state management, and UI rendering
- Written using Preact hooks and functional components
- Validated with Zod schemas
- Tested in Preact Storybook (port 6007)
-
PreactLitAdapter (
/src/web-components/PreactLitAdapter.tsx) - Bridge layer- Base class that extends Lit's
ReactiveElement - Renders Preact components into Lit's shadow DOM using Preact's
render() - Injects context (LAPIS URL, reference genome) via Lit Context API
- Handles CSS injection (Tailwind, DaisyUI, GridJS)
- Variants:
PreactLitAdapter(base),PreactLitAdapterWithGridJsStyles(with table styles)
- Base class that extends Lit's
-
Lit Web Components (
/src/web-components/) - Public API layer- Standards-compliant web components registered as custom elements
- Use Lit decorators (
@customElement,@property) - Each property mirrors the Preact component's props exactly
- Documented in Lit Storybook (port 6006), which is deployed to GitHub pages
Why this pattern? Preact provides excellent DX with hooks, while Lit provides standards-compliant web components that work anywhere.
/src/
├── web-components/ # Lit web component wrappers (public API)
│ ├── visualization/ # Charts, tables, data visualizations
│ ├── input/ # Filter and input components
│ └── wastewaterVisualization/ # Specialized wastewater components
├── preact/ # Preact component implementations (internal)
│ ├── components/ # Shared UI components
│ ├── shared/ # Shared utilities and helpers
│ └── [domain-folders]/ # Domain-specific components
├── operator/ # Data transformation pipeline operators
├── query/ # Query composition functions
├── lapisApi/ # LAPIS backend API client
├── utils/ # General utilities
└── styles/ # Tailwind CSS configuration
The /operator/ directory contains a composable data transformation pipeline inspired by functional programming:
interface Operator<T> {
evaluate(lapis: string, signal?: AbortSignal): Promise<Dataset<T>>;
}Key operators:
FetchAggregatedOperator,FetchDetailsOperator,FetchInsertionsOperator,FetchSubstitutionsOrDeletionsOperator- Fetch from LAPISMapOperator- Transform each item (like Array.map)DivisionOperator- Join two datasets and divide values (for prevalence)GroupByOperator,GroupByAndSumOperator- Group and aggregate dataSortOperator- Sort datasetFillMissingOperator- Fill missing data points (e.g., dates with no data)SlidingOperator- Sliding window aggregation (for smoothing)RenameFieldOperator- Rename fields
Usage: Query functions in /query/ compose operators into pipelines. Operators are lazy (nothing executes until evaluate() is called) and support abort signals for cancellation.
User Interaction
↓
Web Component (Lit) receives props
↓
PreactLitAdapter renders Preact component
↓
Preact component uses useQuery hook
↓
Query function composes Operators
↓
Operator pipeline evaluates against LAPIS
↓
Data flows back through pipeline transformations
↓
Preact component renders with data
↓
Displayed in shadow DOM
Input components fire custom events (e.g., gs-location-changed) that bubble through the DOM. Dashboard maintainers wire these events to update visualization component props. This keeps inputs decoupled from visualizations.
The gs-app component provides global context (LAPIS URL, reference genome, mutation annotations) using Lit's Context API. Child components consume this context via @consume decorator (Lit) or useContext (Preact).
vite.release.config.ts - Library build:
- Entry points:
components(browser) andutil(Node.js compatible) - ES modules only
- Dependencies are external (not bundled)
- Generates TypeScript declarations
- No minification (left to consumers)
vite.release-standalone.config.ts - Standalone build:
- Bundles ALL dependencies into single file
- For use without package manager (CDN, unpkg)
- Output:
/standalone-bundle/dashboard-components.js
"exports": {
"./components": "./dist/components.js", // Web components (browser only)
"./util": "./dist/util.js" // Utilities (Node.js compatible)
}- Unit tests (Vitest): Domain logic in operators, utilities, and helper functions
- Preact Storybook interaction tests: Detailed component testing with user interactions (port 6007)
- Lit Storybook smoke tests: Ensure web component build works (port 6006)
- Playwright tests: Visual regression (screenshots) and CSV download snapshots
Important: All tests use mocked data. Stories must not issue actual HTTP calls to LAPIS to ensure stable tests in CI.
- Use Tailwind CSS and DaisyUI classes whenever possible
- Only use custom CSS if Tailwind/DaisyUI don't work for your use case
- Never import CSS files like
import './my-component.css'- this generates unwanted CSS files in the build output - Instead, apply CSS via Lit's
static stylesfield - Tailwind classes cannot be generated dynamically (Tailwind scans code at build time)
- Develop Preact component in Preact Storybook (port 6007)
- Write interaction tests in Storybook stories
- Create Lit wrapper extending
PreactLitAdapterorPreactLitAdapterWithGridJsStyles - Mirror all Preact props as Lit
@propertydecorated properties - Add to Lit Storybook (port 6006) with basic documentation stories
- Document with JSDoc for custom elements manifest generation
- Run Playwright tests for visual regression
When adding code for package users:
- For web components: Export from
/src/componentsEntrypoint.ts - For utilities/types: Export from
/src/utilEntrypoint.ts - Ensure it's included in the
"files"field of package.json - Generate manifest:
npm run generate-manifest - Update Lit Storybook with documentation
- Component names:
gs-prefix (GenSpectrum) - Event names: Defined in
gsEventNamesconstant - Mutation types:
nucleotidevsamino acid - Temporal granularities:
day,week,month,year