Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
078a5b2
chore: drop Storybook scaffolding
ryu-man Jul 31, 2026
faac90e
chore(tooling): purge Storybook deps, add verify script and CI
ryu-man Jul 31, 2026
5d583bb
style: reformat config files with prettier
ryu-man Jul 31, 2026
e4b1d6a
chore(size-limit): retarget budgets at the full public API
ryu-man Jul 31, 2026
ec7d504
docs: drop per-module READMEs and INDEX.md, refresh root README
ryu-man Jul 31, 2026
5b26790
docs(changelog): reformat headings for markdownlint
ryu-man Jul 31, 2026
7e107ef
refactor(shared): extract frame-batch, frame-tween and inline-style h…
ryu-man Jul 31, 2026
38b17ab
refactor(animate)!: drop the duplicated flip and spring modules
ryu-man Jul 31, 2026
74dd735
refactor(animate,easing): unexport internals and tighten docs
ryu-man Jul 31, 2026
1232ee2
feat(flip): add anchoredFlip
ryu-man Jul 31, 2026
9bea019
refactor(flip)!: consolidate internals and trim the public surface
ryu-man Jul 31, 2026
35f6f20
feat(flip): restore flipTo as the mirror of flipFrom
ryu-man Jul 31, 2026
0163a14
refactor(gestures): split reorder slot math into reorder-internals
ryu-man Jul 31, 2026
f0ecab7
refactor(gestures): unexport internals and share the frame batch
ryu-man Jul 31, 2026
cdb23b1
refactor(morph)!: hide the path-alignment internals
ryu-man Jul 31, 2026
99a50e8
refactor(gradient): unexport splitTopLevel
ryu-man Jul 31, 2026
e1868d7
refactor(presence,scroll,text,variants,view-transition): unexport int…
ryu-man Jul 31, 2026
c7f38da
fix(exports): re-export easing from the root entry and use an explici…
ryu-man Jul 31, 2026
ebd9b67
feat(demo): add MotionLab and dark-mode boot script
ryu-man Jul 31, 2026
2e15bd3
chore(changeset): record the public API trim
ryu-man Jul 31, 2026
2463367
refactor(shared): add listen() and playbackControls, drop DOM-less fa…
ryu-man Aug 15, 2026
a47e927
refactor(animate): use playbackControls and add wireTransform
ryu-man Aug 15, 2026
1de6aac
feat(easing): emit native CSS keywords instead of resampling them
ryu-man Aug 15, 2026
7268474
refactor(timeline): fold timeline-types.ts into timeline.ts
ryu-man Aug 15, 2026
015c2b4
refactor(variants): merge variants.ts into variants.svelte.ts
ryu-man Aug 15, 2026
55e8c01
refactor(presence): share one size-collapse emitter
ryu-man Aug 15, 2026
aa8ffee
refactor(gestures)!: share the DOM plumbing, drop the `disabled` option
ryu-man Aug 15, 2026
5b14abb
feat(flip)!: reactive class/style thunks, always-on layout tracking
ryu-man Aug 15, 2026
f1aa310
docs(demo): show flip({ class }) in MotionLab
ryu-man Aug 15, 2026
dcba23b
chore: fix repository metadata
ryu-man Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/brave-otters-hum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@ixirjs/pulse': minor
---

Emit CSS easing keywords instead of resampling them. An easing that _is_ a CSS
keyword curve (`ease`, `easeIn`, `easeOut`, `easeInOut`, `linear`) now reaches
WAAPI as that keyword rather than a 25-point `linear(…)` approximation of it —
exact timing, smaller keyframes.
18 changes: 9 additions & 9 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
5 changes: 5 additions & 0 deletions .changeset/fuzzy-anchors-flip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ixirjs/pulse': minor
---

Add a lifecycle-safe `anchoredFlip` transition for mounted overlays, including interruption cleanup, virtual reference support, and reduced-motion handling.
19 changes: 19 additions & 0 deletions .changeset/lazy-otters-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@ixirjs/pulse': minor
---

Remove the `disabled` option from every gesture attachment.

It duplicated what Svelte already gives you, and did it worse: `disabled: true`
only short-circuited the setup, so toggling it did nothing to an attachment that
was already live. Conditional attachment is the working form and actually tears
the listeners down:

```svelte
<!-- before -->
<div {@attach draggable({ disabled: !enabled })}>…</div>

<!-- after -->
<div {@attach enabled ? draggable() : undefined}>…</div>
```

5 changes: 5 additions & 0 deletions .changeset/lucky-scopes-shrink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ixirjs/pulse': minor
---

**Breaking:** `createFlipScope()` no longer takes options and no longer returns `clear()`. The `layoutTtlMs` option (and its `CreateFlipScopeOptions` type) is gone — layout records expire after a fixed 250ms, which is already far longer than any unmount/mount handoff, so neither knob had a use. Replace `const { flip, clear } = createFlipScope(opts)` with `const { flip } = createFlipScope()`.
12 changes: 12 additions & 0 deletions .changeset/plenty-moons-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@ixirjs/pulse': minor
---

**Breaking:** trimmed duplicated and internal API off the public surface.

- `flipFromRect`, `flipToRect`, `captureRect`, and the `AnimateFlipRect` type are gone from `@ixirjs/pulse/animate`. They duplicated `flipFrom` / `snapshotRect` in `@ixirjs/pulse/flip`, which carry the richer option set (opacity crossfade, `disablePointerEvents`, distance-derived duration, rect-carrying hooks). Migrate by importing those instead; pass `duration` / `easing` where you passed `AnimateDefaults`.
- `@ixirjs/pulse/flip` no longer exports the delta-math primitives `computeDelta`, `isIdentityDelta`, `diagonal`, `rectsEqual`, or the `FlipDelta` / `DeltaOptions` types. They are internals of the animator. `flip`, `flipFrom`, `snapshotRect`, `measure`, `animateFlip`, `createFlipScope`, `createFlipSwitcher`, and `anchoredFlip` are unchanged.
- `flipTo` stays, as the mirror of `flipFrom`: it animates an element from its current position **to** a captured rect (forward FLIP). `animateFlip({ …, forward: true })` remains the low-level form.
- `@ixirjs/pulse/morph` no longer exports the path-alignment internals `planMorph`, `interpolatePlan`, `toPathString`, `subdivideTo`, `alignSubpaths`, `minimizeAnchorTravel`, `rotateClosed`, `reverseClosed`, or the `MorphPlan` type. `morph`, `parsePath`, and `normalizePath` are unchanged.

Internally, FLIP geometry now lives in one module (`flip/geometry.ts`) instead of being split across the `animate` and `flip` layers.
9 changes: 9 additions & 0 deletions .changeset/tidy-pans-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@ixirjs/pulse': minor
---

Trim the public surface and de-duplicate internals.

- `createFlipAttachment`, `createLayoutBridge`, `createReflowScheduler`, and their types are no longer exported from `@ixirjs/pulse/flip`; they are internals. `flip()` and `createFlipScope()` remain the supported entry points.
- Single RAF batcher (`shared/frame-batch`) now backs both scroll and FLIP reflow scheduling.
- `frameTween` delay is always counted from the first browser frame; the `delayOrigin` option is gone.
17 changes: 17 additions & 0 deletions .changeset/wild-moons-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@ixirjs/pulse': minor
---

Replace `flip()`'s `auto` option with reactive `class` / `style` thunks.

`flip({ class: () => ClassValue })` and `flip({ style: () => string })` let the attachment own
the attribute: it writes the class or inline style itself, then measures and animates the
resulting layout change in the same tick — no discarded thunk and no frame of latency.

Layout auto-tracking (`ResizeObserver` + parent `MutationObserver`) is now always on, so a
shuffled `{#each}` animates with no option at all. Consequently `auto`, the `FlipAuto` type, and
`createObserverManager` / `ObserverManager` are removed from the public API — delete the `auto:`
option from existing call sites.

Note: use flip's `class` option or a dynamic `class={…}` in markup, not both on the same element
— Svelte assigns `className` wholesale and would drop flip's tokens. A static `class="…"` is safe.
2 changes: 1 addition & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
- after each fix or change do a global review of the change and how it is consistent with the codebase
- after each fix or change do a global review of the change and how it is consistent with the codebase
25 changes: 25 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Verify

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: '1.3.9'
- uses: actions/setup-node@v4
with:
node-version: 22
- run: bun install --frozen-lockfile
# One authoritative release-quality command: type/Svelte checks, style,
# browser/server tests, packed-consumer verification, and size budgets.
- run: bun run verify
5 changes: 1 addition & 4 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
Expand Down
30 changes: 15 additions & 15 deletions .size-limit.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[
{
"name": "Root (animate + flip)",
"path": "./dist/index.js",
"limit": "12 kB"
},
{
"name": "animate only",
"path": "./dist/animate/index.js",
"limit": "8 kB"
},
{
"name": "flip only",
"path": "./dist/flip/index.js",
"limit": "6 kB"
}
{
"name": "Root public API",
"path": "./dist/index.js",
"limit": "19 kB"
},
{
"name": "animate only",
"path": "./dist/animate/index.js",
"limit": "8 kB"
},
{
"name": "flip only",
"path": "./dist/flip/index.js",
"limit": "7 kB"
}
]
17 changes: 0 additions & 17 deletions .storybook/main.ts

This file was deleted.

21 changes: 0 additions & 21 deletions .storybook/preview.ts

This file was deleted.

13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

#### `animate(element, props, defaults?)`

- Spring-physics WAAPI animation runtime with per-property spring curves.
- Independent transform component animation (`x`, `y`, `scale`, `rotate`, `skewX`, `skewY`) via registered CSS custom properties — never clobbers sibling transforms.
- `[from, to]` shorthand, `PropConfig` full form, and bare-value (current → target) inputs.
Expand All @@ -18,37 +19,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `prefers-reduced-motion` support (skips animation and applies end-state).

#### `timeline(defaults?)`

- Sequence and parallelize `animate()` calls along a shared clock.
- Position grammar: absolute ms, `"+=N"`/`"-=N"`, `"<"`/`">"` anchors with offsets, named labels.
- `.add()`, `.set()`, `.call()`, `.label()` builder API (chainable).
- Full playback control: `play`, `pause`, `reverse`, `cancel`, `stop`, `seek`, `setPlaybackRate`.
- `finished` promise aggregating all child animations.

#### `spring(options?)`

- Simulate a spring from 0 → 1 and return per-frame samples + duration.
- Memoized by physics parameters (LRU cache, max 128 entries).
- Used internally by `animate()` spring props.

#### `springEasing(options?)`

- Returns a `SpringEasingFn` usable anywhere an `easing` is accepted.
- Carries `.duration` (natural settling time) and `._linearEasing` (pre-built WAAPI string).

#### `stagger(interval, options?)`

- Generate staggered delays for list animations.
- `from`: `'start'` | `'end'` | `'center'` | `number` (normalized 0–1 position).
- Optional `easing` for non-linear wave staggers.

#### `cubicBezier(x1, y1, x2, y2)`

- Build an easing function equivalent to CSS `cubic-bezier(x1, y1, x2, y2)`.
- Newton-Raphson root-finding with binary-subdivision fallback.

#### `easings` namespace

- `linear`, `ease`, `easeIn`, `easeOut`, `easeInOut`
- `quadIn/Out/InOut`, `cubicIn/Out/InOut`, `quartIn/Out/InOut`, `quintIn/Out/InOut`
- `expoIn/Out/InOut`, `sineIn/Out/InOut`, `circIn/Out/InOut`
- `backIn/Out/InOut`, `elasticIn/Out/InOut`, `bounceIn/Out/InOut`

#### `flip(options?)` (Svelte 5 attachment)

- Zero-config FLIP (First, Last, Invert, Play) layout-shift animator via `{@attach flip()}`.
- Reactive remeasure: pass a thunk `auto: () => { void open; }` to track rune dependencies.
- Auto-tracking via `ResizeObserver` + `MutationObserver` (`auto: createObserverManager()`).
Expand All @@ -57,14 +65,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `prefers-reduced-motion` support.

#### `createFlipScope()`

- Shared-element cross-component transitions via `layoutId`.
- `flip` attachment factory bound to a shared layout registry.
- `layoutTtlMs` configurable TTL for stored rects.

#### `flipFrom(element, from, options?)`

- Imperatively animate an element from a captured rect to its current position.

#### `snapshotRect(element)`

- Capture an element's current rect for use with `flipFrom`.

[0.1.0]: https://github.com/svelte-atoms/vibra/releases/tag/v0.1.0
[0.1.0]: https://github.com/ixirjs/pulse/releases/tag/v0.1.0
Loading
Loading