Imported from EOX-A/EOxElements (
AGENTS.md). Install upstream withnpx skills add EOX-A/EOxElements. Copyright stays with the author.
AGENTS.md for EOxElements
Executable reference for AI agents working in this repo. Follow these rules literally.
1. Before Writing Any Code
Answer these questions in order. Stop if you can't answer one.
- Which element is changing? Identify the folder in
/elements/(e.g.,elements/map). - Is this a single-element change? If the request touches multiple elements, state that you will split it into atomic PRs/commits.
- New/changed attributes or properties?
- Attributes: strings, numbers, booleans (HTML compatible).
- Properties: Objects, Arrays, Functions (Complex types).
- What is the minimum change? State it in one sentence.
- Which existing pattern does this follow? (e.g., "following logic extraction pattern in
src/methods/").
If any answer is "I don't know," stop. Ask a clarifying question. Do not guess.
2. Project Overview
EOxElements is an npm workspaces monorepo containing multiple published web component packages under the @eox/ scope, plus a shared utilities package (@eox/elements-utils) and an MCP server (@eox/elements-mcp-server).
- Package name:
@eox/elements(private, not published) - Module system: ESM (
"type": "module") - Node requirement:
>=24.0.0(enforced viaengine-strict=truein.npmrc) - Security Hardening:
ignore-scripts=trueis enabled in.npmrc. This means:postinstallscripts (like Cypress binary download) are skipped. Runnpx cypress installmanually for local testing.prepackscripts are skipped. When publishing or testing packaging, manually runnpm run types:generate.
- Package Quarantine:
min-release-age=7(days) is set in.npmrcto prevent the use of brand-new, potentially unverified package versions. - Component framework: Lit (v3.2+)
- Language: JavaScript with JSDoc type annotations (NOT TypeScript source files)
3. Technology Stack
| Category | Technology |
|---|---|
| Component framework | Lit 3.2+ (LitElement) |
| Language | JavaScript + JSDoc (type-checked by TypeScript) |
| Bundler | Vite (library mode per element) |
| Type checker | TypeScript (checkJs mode, no .ts source) |
| Test runner | Cypress (component + E2E) |
| Component mounting | cypress-lit |
| Coverage | Istanbul (vite-plugin-istanbul) + NYC |
| Storybook | @storybook/web-components-vite (v10) |
| Linting | ESLint (flat config) + @eox/eslint-config |
| Formatting | Prettier |
| Release | release-please (per-package automated releases) |
| CI/CD | GitHub Actions |
| Design system | @eox/ui (CSS variables + fonts) |
4. Architectural Boundaries & Patterns
Element Anatomy
Every element follows a consistent directory layout:
elements/<name>/
├── package.json # @eox/<name>, main: ./src/main.js
├── vite.config.js # Vite library build config
├── tsconfig.build.json # TypeScript config for .d.ts generation
├── src/
│ ├── main.js # Main LitElement class + customElements.define()
│ ├── types.ts # TypeScript type definitions (if needed)
│ ├── style.js # Base/functional CSS (always applied)
│ ├── style.eox.js # Branded/themed CSS (unless `unstyled`)
│ ├── enums/ # Constants and defaults (e.g. index.js, frozen objects)
│ ├── helpers/ # Pure utility functions
│ ├── methods/ # Extracted component logic (e.g., domain/index.js)
│ └── components/ # Sub-components (if decomposed)
├── stories/ # Storybook stories (*.stories.js)
└── test/ # Cypress component tests (*.cy.js)
├── cases/ # Test case implementations (extracted logic)
└── fixtures/ # JSON/image test fixtures
Web Component Definition Pattern
Every element follows this structure in src/main.js:
import { LitElement, html, nothing } from "lit";
// ... imports
/**
* @element eox-example
* @fires {CustomEvent} change - Fired when the value changes
*/
export class EOxExample extends LitElement {
static get properties() {
return {
prop: { attribute: false, type: Array },
unstyled: { type: Boolean },
};
}
constructor() {
super();
this.prop = [];
this.unstyled = false;
}
// Lifecycle and Logic...
render() {
return html`
<style>
${style} ${!this.unstyled && styleEOX}
</style>
<div>...</div>
`;
}
}
customElements.define("eox-example", EOxExample);
Key Rules for Implementation
- Monorepo Commands: Always run
lint:fix,typecheck, andformatfrom the root of the repository. Do not run them inside element sub-directories as they might lack the necessary scripts or configuration. - JSDoc Event Listeners: In custom inputs/editors, cast custom event listener callbacks with
/** @type {EventListener} */and events with/** @type {CustomEvent} */when accessinge.detailto preventtsctypecheck errors. - No TypeScript Code: Use JSDoc for types. Do NOT use TypeScript decorators (
@property,@state) or.tsfiles for logic. TypeScript is used only for type definitions (types.ts,@typedef). - Reactive Properties: Always use
static get properties()orstatic properties = {}. Lit decorators are NOT used anywhere in the codebase. Useattribute: falsefor complex Object/Array types. - Method Delegation (Most Important Pattern): Extract business logic to standalone functions in
src/methods/<domain>/directories.- Functions MUST receive the component instance (
this) as a parameter, e.g.,export function setCenterMethod(center, EOxMap) { ... }. - The component class calls these from its setters/lifecycle, passing
this. - Re-export methods via barrel
index.jsfiles.
- Functions MUST receive the component instance (
- Enums & Constants: Do NOT use TypeScript enums. Constants live in
src/enums/as frozen JavaScript objects withSCREAMING_SNAKE_CASEnaming. - Projections: For vector sources with a specific projection (e.g., GeoJSON in EPSG:3035), ensure the projection is registered globally. Prefer using
registerProjectionwith a direct proj4 definition string for stability in stories, orregisterProjectionFromCodefor dynamic environments. In the layer definition, use theformatobject to specifydataProjection.- Normalization: When comparing projections, always normalize to string codes (e.g.,
typeof p === 'string' ? p : p.getCode()). - Coordinates & Longitude: For wrap-around or antimeridian logic, always normalize longitudes to the
[-180, 180]range before magnitude comparison. - Extent Transformation: Use
transformExtentwithstops(densification) for non-linear or polar transformations to ensure accurate bounding box calculations.
- Normalization: When comparing projections, always normalize to string codes (e.g.,
- Inter-Component Communication: Components depending on
<eox-map>use a standardizedforproperty (a CSS selector defaulting to"eox-map"). InfirstUpdated(), callgetElement(this.for)from@eox/elements-utilsto resolve the reference, even across shadow boundaries. Address re-resolution on property change inupdated(). - Styling:
- EOxUI First: This repository uses EOxUI. Prefer using proper HTML structure and EOxUI classes over writing custom CSS.
- Minimal Custom CSS: Only write custom CSS if the requirement cannot be solved using EOxUI.
- Common Styles: Use
addCommonStylesheet()from@eox/elements-utilsin all elements. - Isolation: Prefer
cssliteral for component-specific styles if absolutely necessary.
- Shadow DOM: Use
createRenderRoot() { return this.noShadow ? this : super.createRenderRoot(); }if needed. Usebubbles: true, composed: truewhen events need to cross shadow DOM boundaries. - Testing Structure:
.cy.jsfiles definedescribe/itstructure only. Test logic MUST be extracted into standalone functions insidetest/cases/and re-exported via barrierindex.jsfiles. There is no Jest/Vitest. Web components are mounted viacypress-lit.
Documentation & Stories
Documentation (The "Doc-as-Source" Rule)
EOxElements is integrated with an MCP Server. Documentation is code.
- Events: You MUST use
@firesin the class JSDoc AND include a JSDoc comment directly above thedispatchEventcall for it to be indexed. - Methods: Public methods must have full JSDoc (params, return types).
- Custom Elements Manifest: Ensure your JSDoc is compatible with
@custom-elements-manifest/analyzer.
Stories (/elements/*/stories/)
- Every new feature MUST have a story in
[element].stories.js. - Stories must include a fully functional code example in the description (rendered in Storybook Docs).
- JSDoc at Story Level: Use JSDoc to explain the specific use case the story demonstrates. The text must include enough information so that a coding agent can find and use the story as an example for including the EOxElement in a downstream application.
5. Implementation Workflow
Touch only what the request requires. Do not refactor adjacent components.
Step-by-Step Execution Plan
Before starting, state your plan:
- [Logic] -> verify: [run component test]
- [UI/Styles] -> verify: [npm run lint:fix]
- [Docs/Stories] -> verify: [check JSDoc/story presence]
Quality Control Pipeline
After every implementation, you MUST run:
- Component Tests: Run tests from the root level for the specific element workspace.
- Run all component tests (if necessary):
npm run test:component - Run tests for a specific element (preferred):
npx cypress run --component --spec "elements/[element]/test/*.cy.js"
- Run all component tests (if necessary):
- Linting: Run
npm run lint:fixfrom the root to fix style issues. - Type Checking: Run
npm run typecheckto ensure type safety. - Formatting: Run
npm run formatto ensure consistent code style.
6. Commit & PR Rules (Strict)
We use release-please. Formatting errors here will break the release pipeline.
- PR Title: Must be in Conventional Commit format (e.g.,
feat: add tooltip property). It is not needed to use the element name in the title scope (e.g.,fix(drawtools): ...is not required) sincerelease-pleasehandles the changelog/release of the element automatically. Instead, use standard scopes or no scopes (e.g.,fix: fixed a thingorfix(style): ...). - Commit Message: Same as PR Title. Since we squash, the PR title becomes the commit.
- One Element Per PR: Never mix changes for e.g.
mapandlayercontrolin one PR. - One Feature/Fix Per PR: Never mix different features or fixes into one PR (even if they concern the same element). Each PR is squashed and represents exactly one entry in the changelog.
7. Troubleshooting (When things fail)
- Lint failure: Read the error message literally. Fix only the flagged line. Re-run
npm run lint:fix. - Typecheck failure: Ensure types are defined in
types.tsorsrc/main.jsusing@typedef. - Event not showing in Storybook: Ensure the
dispatchEventhas a JSDoc block immediately above it.
8. Pre-Submission Checklist
- I changed only ONE element folder.
- Every property/attribute has a JSDoc description.
- Every new event has an
@firestag AND a JSDoc block abovedispatchEvent. - Functional story added to
stories/with a code example. -
npm run lint:fix,typecheck, andformatpassed. - PR title/commit message follows
type(scope): messageformat. - PR description follows the repository template (
.github/pull_request_template.md). - I have verified that
addCommonStylesheet()is used for common styles.
9. Common Pitfalls
- Do NOT use TypeScript source files — write
.jswith JSDoc annotations. Only use.tsfor type definitions. - Do NOT use Lit decorators — use
static get properties()orstatic properties = {}instead. - Do NOT create custom base classes or mixins — extend
LitElementdirectly and use the method extraction pattern for shared logic. - Do NOT use centralized state management — use LitElement's reactive properties and custom events.
- Do NOT forget
addCommonStylesheet()— call it instyle.eox.jsfor consistent global styles. - Do NOT skip the
unstyledproperty — every component must support disabling branded styling. - Do NOT register custom elements anywhere other than the bottom of
main.js. - Do NOT use default exports for component classes — use named exports.
- Always use barrel
index.jsfiles inhelpers/,methods/, andenums/directories. - Always pass the component instance as a parameter to extracted method functions (never rely on
thisbinding in separate files).