Feat/carousel impl #1

Merged
ilia merged 11 commits from feat/carousel-impl into main 2026-07-01 06:30:09 +00:00
Showing only changes of commit 4424934962 - Show all commits
+96
View File
@@ -0,0 +1,96 @@
# @ilia/carousel
Tiny, framework-agnostic scroll-snap carousel. Zero runtime deps. Native scroll
handles touch/momentum, CSS handles snap + motion, JS is a thin layer.
- **core** ≤1 KB gzip · **dots** ≤0.6 KB · **autoplay** ≤0.6 KB
- Ships **unminified** ESM — your bundler minifies.
## Install
This package lives on a private Gitea registry. Point the `@ilia` scope at it:
```
# .npmrc
@ilia:registry=https://git.allmy.work/api/packages/ilia/npm/
```
```bash
yarn add @ilia/carousel
```
## Entry points
| Import | What |
|---|---|
| `@ilia/carousel` | `createCarousel(track)` core |
| `@ilia/carousel/dots` | `dots()` — Firefox/legacy dot fallback (self-gates) |
| `@ilia/carousel/autoplay` | `autoplay()` — reduce-motion-aware auto-advance |
| `@ilia/carousel/carousel.css` | snap track, native markers, opt-in `.fx` effect |
## Usage
```html
<div class="track" id="carousel">
<div class="slide"></div>
<div class="slide"></div>
<div class="slide"></div>
</div>
<button id="prev" aria-label="Previous"></button>
<button id="next" aria-label="Next"></button>
<!-- Firefox/legacy dot fallback; native ::scroll-marker owns dots elsewhere -->
<div id="dots" aria-label="Choose slide">
<button aria-label="Slide 1"></button>
<button aria-label="Slide 2"></button>
<button aria-label="Slide 3"></button>
</div>
```
```ts
import { createCarousel } from '@ilia/carousel';
import { dots } from '@ilia/carousel/dots';
import { autoplay } from '@ilia/carousel/autoplay';
import '@ilia/carousel/carousel.css';
const track = document.getElementById('carousel');
const c = createCarousel(track);
// Arrows are just your own handlers — no module needed.
document.getElementById('next').onclick = () => c.next();
document.getElementById('prev').onclick = () => c.prev();
// Dots: no-ops where native ::scroll-marker exists (no double dots).
dots(c, document.getElementById('dots'));
// Opt-in auto-advance. Provide a visible pause control (see a11y note).
const stop = autoplay(c, { interval: 4000, root: track });
```
Add the opt-in scroll-driven effect with `class="track fx"` — it degrades to plain
snap where `animation-timeline: view()` is unsupported.
## Accessibility
- `autoplay()` **never starts** under `prefers-reduced-motion: reduce`, and the `.fx`
effect + smooth scrolling are gated behind `@media (prefers-reduced-motion: no-preference)`.
- If you use `autoplay()`, you **must** provide a visible pause/stop control
(WCAG 2.2.2) — wire it to the returned `stop()`.
- Give dot buttons accessible labels (`aria-label`).
## Browser support
| Feature | Chrome | Safari | Firefox |
|---|---|---|---|
| `scroll-snap` + core path | ✅ | ✅ | ✅ |
| `::scroll-marker` native dots | ✅ 135+ | ✅ 18.2+ | ⚠️ → JS `dots()` fallback |
| scroll-driven `.fx` | ✅ | ⚠️ partial → plain snap | ✅ |
Index tracking uses IntersectionObserver (universal today); a `scrollsnapchange`
upgrade is deferred until Firefox ships it.
## Release
```bash
npm version <patch|minor|major>
git push --follow-tags # CI verifies, runs e2e, publishes on the v* tag
```