This file provides guidance when working with code in this repository.
Compotes is a component library focused on customization and accessibility. It provides framework-agnostic UI components (collapse, drag, drilldown, dropdown, marquee) with minimal CSS to allow maximum customization.
This is a pnpm workspace monorepo with catalog mode enabled (catalogMode: strict):
- packages/core (
compotes): Vanilla JavaScript/TypeScript components (class-based) - packages/vue (
@compotes/vue): Vue 3 wrapper components - docs/: VitePress documentation site
Dependencies are managed via pnpm catalog in pnpm-workspace.yaml. Use catalog: in package.json to reference catalog versions.
pnpm build # Build all packages
pnpm -F compotes build # Build core package only
pnpm -F @compotes/vue build # Build Vue package onlypnpm test # Run all tests (types + vitest)
pnpm test:vitest # Run vitest tests across all packages
pnpm test:types # Run type checks only
pnpm -F compotes test:vitest # Run tests for core package onlyTests use Vitest with browser testing via Playwright. Browser tests run against Chromium, Firefox, and WebKit.
pnpm lint # Lint all files
pnpm lint:fix # Lint and auto-fixUses @antfu/eslint-config.
pnpm docs # Start VitePress dev server
pnpm docs:build # Build documentation
pnpm docs:serve # Preview built docsComponents follow a class-based architecture:
-
Parent Base Class (
src/components/_parent.ts):- All components extend
Parent<E, O>abstract class - Provides lifecycle methods:
init(),destroy() - Event system using custom events with naming pattern:
c.{componentName}.{eventName} - Event cleanup via AbortController
registerEvent()for automatic cleanup on destroy- Error handling via custom ErrorCompotes class
- Options interface with
initandonevent listeners
- All components extend
-
Component Pattern:
- Constructor accepts element (selector or HTMLElement) and options
initElements(): Query and store DOM element referencesinitEvents(): Register event listeners usingregisterEvent()initAccessibilityAttrs(): Set ARIA attributes for accessibility- Public methods for component actions (show, hide, toggle, etc.)
- Protected
emitEvent()for custom events
-
CSS Architecture:
- Components use BEM-like naming:
c-{component},c-{component}--{modifier} - Trigger/action elements:
c-{component}-{action}(e.g.,c-collapse-trigger) - Individual CSS files per component in
src/css/ - CSS can be imported separately:
compotes/css/{component}.css
- Components use BEM-like naming:
-
Build Configuration:
- Uses tsdown with custom plugin to emit individual CSS files
- Outputs ESM and UMD formats
- LightningCSS for CSS minification
- Global name:
compotes(for UMD)
Vue components wrap core components using a composable pattern:
-
Component Structure:
- Each Vue component (e.g.,
CCollapse.vue) is a thin wrapper - Uses corresponding composable (e.g.,
useCollapse()) - Provides Vue-friendly API: props, emits, expose
- Supports
asprop for custom element rendering - Auto-generates stable IDs for accessibility
- Each Vue component (e.g.,
-
Composables (
src/composables/):- All composables extend
useParent()base composable - Create and manage core component instances
- Return reactive state + action methods
- Sync Vue reactive state with core component state via event listeners
- Use
markRaw()for component instances to avoid reactivity overhead - Use
shallowReactive()for state to minimize reactivity
- All composables extend
-
Context System:
- Uses Vue's provide/inject for component communication
- Context keys defined in
src/components/context.ts - Example: Collapse trigger components inject collapse ID from parent
-
Core package:
- Create component class extending
Parentinsrc/components/{name}.ts - Define Events enum and ComponentOptions interface
- Add CSS file in
src/css/{name}.css - Export from
src/index.ts - Add CSS export to
package.jsonexports field - Document in
docs/guide/{name}.md(options, methods, events, data)
- Create component class extending
-
Vue package:
- Create composable in
src/composables/{name}.tsextendinguseParent() - Create component(s) in
src/components/C{Name}.vue - Add context key if needed in
src/components/context.ts - Export from
src/index.ts - Document component in
docs/guide/vue/components.md(props, events, exposed) - Document composable in
docs/guide/vue/composables.md(list table, state section)
- Create composable in
Custom events follow the pattern: c.{componentName}.{eventName}
Example: c.collapse.show, c.collapse.hidden, c.drilldown.next
Declare global event types:
declare global {
interface HTMLElementEventMap extends Record<`c.component.${Events}`, CustomEvent<Component>> {}
}- Use
initAccessibilityAttrs()to set ARIA attributes - Common attributes:
aria-controls,aria-expanded,role="button" - Use
tabbablepackage for focus management - Utility functions in
src/utils/accessibility.ts(focusFirst, focusLast, focusSibling, etc.)
- Use
getTransitionDuration()fromsrc/utils/animation.tsto respect CSS transitions - Set collapsing/transitioning states during animations
- Clean up with
clearTimeout()before new animations
- Node.js 24 required
- pnpm is the package manager (use pnpm, not npm or yarn)
- Vue package has peer dependency on Vue >= 3.0.0
- Core components work in any JavaScript environment (no framework required)
- CSS is intentionally minimal for customization