diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 22a7397..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -docs/.vitepress/cache \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..6fcc61e --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,17 @@ +name: Setup +description: Install pnpm, Node.js and dependencies + +runs: + using: composite + steps: + - uses: pnpm/action-setup@v4 + + - name: Use Node.js 24 + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install + shell: bash + run: pnpm install diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89be2ed..34f3104 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,89 +12,59 @@ on: jobs: test: runs-on: ubuntu-latest + name: Test & Coverage steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - - uses: pnpm/action-setup@v2 - with: - version: 8 - - - name: Use Node.js 20 - uses: actions/setup-node@v3 - with: - node-version: 20 - cache: pnpm - - - name: Install - run: pnpm install + - uses: ./.github/actions/setup - name: Playwright Install - run: npx playwright install + run: npx playwright install --with-deps - name: Build run: pnpm build - name: Test - run: pnpm test + run: pnpm test:coverage - # coverage: - # runs-on: ubuntu-latest - # name: 'Coverage' + - name: Coveralls + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} - # steps: - # - uses: actions/checkout@v3 - # with: - # fetch-depth: 0 + lint: + runs-on: ubuntu-latest + name: Lint + + steps: + - uses: actions/checkout@v6 - # - uses: pnpm/action-setup@v2.2.4 - # with: - # version: 7 + - uses: ./.github/actions/setup - # - name: Set node version to 16 - # uses: actions/setup-node@v3 - # with: - # node-version: 16 - # cache: pnpm + - name: Lint + run: pnpm lint - # - name: Install - # run: pnpm install + docs-sync: + runs-on: ubuntu-latest + name: Docs Sync - # - name: Build - # run: pnpm build + steps: + - uses: actions/checkout@v6 - # - name: Test - # run: pnpm coverage + - uses: ./.github/actions/setup - # - name: Coveralls - # uses: coverallsapp/github-action@master - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check docs are in sync + run: pnpm docs:sync - lint: + build: runs-on: ubuntu-latest - name: 'Lint: node-18, ubuntu-latest' + name: Build steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 + - uses: actions/checkout@v6 - - uses: pnpm/action-setup@v2 - with: - version: 8 - - - name: Set node version to 20 - uses: actions/setup-node@v3 - with: - node-version: 20 - cache: pnpm - - - name: Install - run: pnpm install + - uses: ./.github/actions/setup - name: Build run: pnpm build - - - name: Lint - run: pnpm lint diff --git a/.gitignore b/.gitignore index 075aae3..50eb814 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ reports coverage *.lcov .nyc_output +__screenshots__ # VSCode .vscode @@ -50,4 +51,8 @@ Network Trash Folder Temporary Items .apdisk -docs/.vitepress/cache \ No newline at end of file +docs/.vitepress/cache + +# CLAUDE +.claude +CLAUDE.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..98debdf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,164 @@ +# CLAUDE.md / AGENTS.md + +This file provides guidance when working with code in this repository. + +## Project Overview + +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. + +## Monorepo Structure + +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. + +## Common Commands + +### Building +```bash +pnpm build # Build all packages +pnpm -F compotes build # Build core package only +pnpm -F @compotes/vue build # Build Vue package only +``` + +### Testing +```bash +pnpm 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 only +``` + +Tests use Vitest with browser testing via Playwright. Browser tests run against Chromium, Firefox, and WebKit. + +### Linting +```bash +pnpm lint # Lint all files +pnpm lint:fix # Lint and auto-fix +``` + +Uses `@antfu/eslint-config`. + +### Documentation +```bash +pnpm docs # Start VitePress dev server +pnpm docs:build # Build documentation +pnpm docs:serve # Preview built docs +``` + +## Architecture + +### Core Package (packages/core) + +Components follow a class-based architecture: + +1. **Parent Base Class** (`src/components/_parent.ts`): + - All components extend `Parent` 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 `init` and `on` event listeners + +2. **Component Pattern**: + - Constructor accepts element (selector or HTMLElement) and options + - `initElements()`: Query and store DOM element references + - `initEvents()`: Register event listeners using `registerEvent()` + - `initAccessibilityAttrs()`: Set ARIA attributes for accessibility + - Public methods for component actions (show, hide, toggle, etc.) + - Protected `emitEvent()` for custom events + +3. **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` + +4. **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 Package (packages/vue) + +Vue components wrap core components using a composable pattern: + +1. **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 `as` prop for custom element rendering + - Auto-generates stable IDs for accessibility + +2. **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 + +3. **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 + +## Development Guidelines + +### Adding a New Component + +1. **Core package**: + - Create component class extending `Parent` in `src/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.json` exports field + - Document in `docs/guide/{name}.md` (options, methods, events, data) + +2. **Vue package**: + - Create composable in `src/composables/{name}.ts` extending `useParent()` + - 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) + +### Event Naming Convention + +Custom events follow the pattern: `c.{componentName}.{eventName}` + +Example: `c.collapse.show`, `c.collapse.hidden`, `c.drilldown.next` + +Declare global event types: +```typescript +declare global { + interface HTMLElementEventMap extends Record<`c.component.${Events}`, CustomEvent> {} +} +``` + +### Accessibility Patterns + +- Use `initAccessibilityAttrs()` to set ARIA attributes +- Common attributes: `aria-controls`, `aria-expanded`, `role="button"` +- Use `tabbable` package for focus management +- Utility functions in `src/utils/accessibility.ts` (focusFirst, focusLast, focusSibling, etc.) + +### Animation Handling + +- Use `getTransitionDuration()` from `src/utils/animation.ts` to respect CSS transitions +- Set collapsing/transitioning states during animations +- Clean up with `clearTimeout()` before new animations + +## Important Notes + +- 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 diff --git a/README.md b/README.md index be3c56d..e42bb16 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ npm package build status +[![Coverage Status](https://coveralls.io/repos/github/Applelo/compotes/badge.svg?branch=master)](https://coveralls.io/github/Applelo/compotes?branch=master) # Compotes @@ -12,12 +13,15 @@ To learn more, check the *[documentation](https://compotes.dev)*. - ๐Ÿ‘จโ€๐ŸŽจ Minimal CSS to do your customization - ๐Ÿฆพ Accessibility in mind - ๐Ÿ“  Fully typed +- ๐Ÿงฉ Framework agnostic +- ๐Ÿ“ฆ Simple components ready to use +- ๐Ÿชถ Lightweight ## ๐Ÿ Compotes -- Collapse/Accordion ([demo](https://compotes.dev/demo/drilldown.html)) +- Collapse/Accordion ([demo](https://compotes.dev/demo/collapse.html)) - Drag ([demo](https://compotes.dev/demo/drag.html)) -- Drilldown ([demo](https://compotes.dev/demo/collapse.html)) +- Drilldown ([demo](https://compotes.dev/demo/drilldown.html)) - Dropdown ([demo](https://compotes.dev/demo/dropdown.html)) - Marquee ([demo](https://compotes.dev/demo/marquee.html)) - -> Vue 3/Nuxt 3 packages will arrive soon stay tuned! +
--> ## ๐Ÿ™‹โ€โ™‚๏ธ Why ? -A lot of components library are already shipped with styles but as a Front End developer, I always wants to override a lot. Futhermore, there are not always accessible or they are shipped with jQuery. +A lot of component libraries are already shipped with styles but as a Front End developer, I always want to override a lot. Furthermore, they are not always accessible or they are shipped with jQuery. + +There are some good libraries like [React Aria](https://react-spectrum.adobe.com/react-aria/react-aria-components.html) but it's made to work on one framework and I work on different tech like Wordpress, Symfony or VueJS. + +> This library provides only the compotes and it's up to you to make a tart. -There are some good library like [React Aria](https://react-spectrum.adobe.com/react-aria/react-aria-components.html) but it's made to work on one framework and I work on different tech like Wordpress, Symfony or VueJS. +## AI Usage -> This library provides only the compotes and it's you to make a tart. +This project uses AI to improve and keep the documentation up to date. It also helps me writing the connection between core components to VueJS. ## ๐Ÿ‘จโ€๐Ÿ’ผ License diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index f938711..6ea98ff 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -4,6 +4,9 @@ import { defineConfig } from 'vitepress' export default defineConfig({ title: 'Compotes', description: 'Components library focused on accessibility/customization', + head: [ + ['link', { rel: 'icon', type: 'image/svg+xml', href: 'data:image/svg+xml,๐Ÿฏ' }], + ], themeConfig: { footer: { message: 'Released under the MIT License.', @@ -12,51 +15,71 @@ export default defineConfig({ nav: [ { text: 'Home', link: '/' }, { text: 'Guide', link: '/guide/' }, - { text: 'Demo', link: '/demo/collapse' }, + { text: 'Demo', link: '/demo/' }, ], search: { provider: 'local', }, - sidebar: [ - { - text: 'Guide', - items: [ - { text: 'Get started', link: '/guide/' }, - { - text: 'Collapse / Accordion', - link: '/guide/collapse', - }, - { - text: 'Drag', - link: '/guide/drag', - }, - { - text: 'Drilldown', - link: '/guide/drilldown', - }, - { - text: 'Dropdown', - link: '/guide/dropdown', - }, - { - text: 'Marquee', - link: '/guide/marquee', - }, - ], - }, - { - text: 'Demo', - collapsed: true, - items: [ - { text: 'Collapse/Accordion', link: '/demo/collapse' }, - { text: 'Drag', link: '/demo/drag' }, - { text: 'Drilldown', link: '/demo/drilldown' }, - { text: 'Dropdown', link: '/demo/dropdown' }, - { text: 'Marquee', link: '/demo/marquee' }, - ], - }, - ], - + sidebar: { + '/guide/': [ + { + text: 'Guide', + items: [ + { text: 'Get started', link: '/guide/' }, + { + text: 'Collapse / Accordion', + link: '/guide/collapse', + }, + { + text: 'Drag', + link: '/guide/drag', + }, + { + text: 'Drilldown', + link: '/guide/drilldown', + }, + { + text: 'Dropdown', + link: '/guide/dropdown', + }, + { + text: 'Marquee', + link: '/guide/marquee', + }, + ], + }, + { + text: 'Vue', + items: [ + { + text: 'Get started', + link: '/guide/vue/', + }, + { + text: 'Composables', + link: '/guide/vue/composables', + }, + { + text: 'Components', + link: '/guide/vue/components', + }, + ], + }, + ], + '/demo/': [ + { + text: 'Demo', + link: '/demo/', + items: [ + { text: 'Collapse/Accordion', link: '/demo/collapse' }, + { text: 'Drag', link: '/demo/drag' }, + { text: 'Drilldown', link: '/demo/drilldown' }, + { text: 'Dropdown', link: '/demo/dropdown' }, + { text: 'Marquee', link: '/demo/marquee' }, + ], + }, + ], + }, socialLinks: [ { icon: 'github', link: 'https://github.com/Applelo/compotes' }, ], diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index b49f38d..066ac50 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,6 +1,6 @@ // https://vitepress.dev/guide/custom-theme -import { h } from 'vue' import Theme from 'vitepress/theme' +import { h } from 'vue' import './style.css' export default { diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index 6b27723..61e2296 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -15,6 +15,7 @@ --vp-c-brand-dark: #535bf2; --vp-c-brand-darker: #454ce1; --vp-c-brand-dimm: rgba(100, 108, 255, 0.08); + --vp-c-brand-1: var(--vp-c-brand); } /** diff --git a/docs/demo/collapse.md b/docs/demo/collapse.md index ea27a0a..767f093 100644 --- a/docs/demo/collapse.md +++ b/docs/demo/collapse.md @@ -6,16 +6,7 @@ prev: false
- -
+ +

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia facere possimus impedit facilis culpa illo earum deserunt consequuntur minus. Ad et qui labore reprehenderit magnam exercitationem placeat magni nesciunt suscipit.

-
+
- -
+ +

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia facere possimus impedit facilis culpa illo earum deserunt consequuntur minus. Ad et qui labore reprehenderit magnam exercitationem placeat magni nesciunt suscipit.

-
+
diff --git a/docs/demo/drag.md b/docs/demo/drag.md index 75fc3df..fbe3602 100644 --- a/docs/demo/drag.md +++ b/docs/demo/drag.md @@ -2,13 +2,7 @@
-
+

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia facere possimus impedit facilis culpa illo earum deserunt consequuntur minus. Ad et qui labore reprehenderit magnam exercitationem placeat magni nesciunt suscipit. @@ -48,14 +42,14 @@ Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia facere possimus impedit facilis culpa illo earum deserunt consequuntur minus. Ad et qui labore reprehenderit magnam exercitationem placeat magni nesciunt suscipit.

-
+ - +
diff --git a/docs/demo/drilldown.md b/docs/demo/drilldown.md index 612818a..42da0d4 100644 --- a/docs/demo/drilldown.md +++ b/docs/demo/drilldown.md @@ -2,14 +2,12 @@ - + + diff --git a/docs/demo/dropdown.md b/docs/demo/dropdown.md index 3b26927..20d5782 100644 --- a/docs/demo/dropdown.md +++ b/docs/demo/dropdown.md @@ -2,16 +2,7 @@ - + + Basic Dropdown + + Lorem ipsum dolor sit amet + + - + + diff --git a/docs/demo/index.md b/docs/demo/index.md new file mode 100644 index 0000000..ad54f60 --- /dev/null +++ b/docs/demo/index.md @@ -0,0 +1,7 @@ +# Demo + +Compotes provides minimal styles, so additional CSS has been added for the demos to integrate with VitePress styling. + +Demos are made with the [VueJS components](/guide/vue/components) of the library. + +You can check directly [the source code of the documentation](https://github.com/Applelo/compotes/tree/main/docs/demo) for more details. diff --git a/docs/demo/marquee.md b/docs/demo/marquee.md index e13793b..0bd127c 100644 --- a/docs/demo/marquee.md +++ b/docs/demo/marquee.md @@ -2,26 +2,7 @@ -
- -
-
-
    -
  • Marquee Left Direction
  • -
  • -
-
-
-
    -
  • Marquee Bottom Direction
  • -
-
-
-
    -
  • Alternate
  • -
-
+ +
  • This is the default marquee
  • +
  • Marquee or marquii
  • +
    + +
  • Marquee Left Direction
  • +
  • +
    + +
  • Marquee Bottom Direction
  • +
    + +
  • Alternate
  • +
    diff --git a/docs/guide/collapse.md b/docs/guide/collapse.md index b1a7fc0..95e619b 100644 --- a/docs/guide/collapse.md +++ b/docs/guide/collapse.md @@ -1,6 +1,6 @@ # Collapse / Accordion -The collapse component allows to collapse any elements you want. You can make an accordion with it for example. +The collapse component lets you show and hide content with optional CSS transitions. It can be used to build accordions. > This component is inspired by the [Collapse](https://getbootstrap.com/docs/5.3/components/collapse/) component from Bootstrap. @@ -14,7 +14,7 @@ import { Collapse } from 'compotes' const collapse = new Collapse('#my-collapse') ``` -It is recommended to put an `id` to the element you want to collapse. To all your trigger buttons, add an `aria-controls` attribute refering to the `id` of the collapse. +It is recommended to add an `id` to the element you want to collapse. To all your trigger buttons, add an `aria-controls` attribute referring to the `id` of the collapse. ```html