diff --git a/.changeset/yellow-donkeys-own.md b/.changeset/yellow-donkeys-own.md
new file mode 100644
index 00000000..e5a8555c
--- /dev/null
+++ b/.changeset/yellow-donkeys-own.md
@@ -0,0 +1,5 @@
+---
+'@svelte-put/swipeable': major
+---
+
+First implementation with dedicated docs page
diff --git a/packages/clickoutside/src/index.js b/packages/clickoutside/src/index.js
index 5fd9fe96..34fe1286 100644
--- a/packages/clickoutside/src/index.js
+++ b/packages/clickoutside/src/index.js
@@ -1,5 +1,5 @@
// Copyright (c) Quang Phan. All rights reserved. Licensed under the MIT license.
export * from './clickoutside.js';
-export * from './types.public';
+export * from './types.public.js';
diff --git a/packages/swipeable/CHANGELOG.md b/packages/swipeable/CHANGELOG.md
new file mode 100644
index 00000000..4dc68c6f
--- /dev/null
+++ b/packages/swipeable/CHANGELOG.md
@@ -0,0 +1,2 @@
+# Changelog
+
diff --git a/packages/swipeable/README.md b/packages/swipeable/README.md
new file mode 100644
index 00000000..2ef8de64
--- /dev/null
+++ b/packages/swipeable/README.md
@@ -0,0 +1,68 @@
+
+
+# `@svelte-put/swipeable`
+
+[![npm.badge]][npm] [![bundlephobia.badge]][bundlephobia] [![docs.badge]][docs]
+
+Svelte action `use:swipeable` - event for setting up quick gestures on an element (swipe right to delete, for example).
+
+
+
+## `svelte-put`
+
+This package is part of the [@svelte-put][github.monorepo] family. For contributing guideline and more, refer to its [readme][github.monorepo].
+
+## Usage & Documentation
+
+[See the dedicated documentation page here][docs].
+
+## Quick Start
+
+```html
+
+
+
+```
+
+## Demo
+
+The following example demonstrates a practical use case for `swipeable` to implement swipe-to-delete or swipe-to-archive, often seen in notification center or email apps.
+
+
+
+```svelte src=./_page/examples/demo.svelte title=demo.svelte
+```
+
+## Events
+
+`swipeable` fires `swipestart` when a swipe action in one of the allowed directions is detected (pointermove), and `swipeend` when the swipe action is completed (pointerup).
+
+```typescript title="SwipeableAttributes.d.ts"
+interface SwipeStartEventDetail {
+ /** direction of this swipe action */
+ direction: SwipeSingleDirection;
+ /** travel distance of this swipe action in px */
+ distance: number;
+}
+interface SwipeEndEventDetail extends SwipeStartEventDetail {
+ /** whether the swipe action passes the threshold, or is a flick */
+ passThreshold: boolean;
+}
+
+interface SwipeableAttributes {
+ onswipestart?: (event: CustomEvent) => void;
+ onswipeend?: (event: CustomEvent) => void;
+}
+```
+
+
+
+**Multiple `swipestart` events**
+
+A `swipestart` event might be fired again if user changes the swipe direction during the swipe action. In [Demo], try swiping to left and then change to right midway. Observe that the background color and icon are updated accordingly.
+
+
+
+## Configuration
+
+`swipeable` takes an optional parameter with the following interface. Details of each property are explained in next sections.
+
+```typescript title="SwipeableParameter.d.ts"
+type SwipeableParameter = SwipeableConfig['direction'] | SwipeableConfig | undefined;
+
+interface SwipeableConfig {
+ direction?: SwipeDirection | SwipeDirection[];
+ threshold?: SwipeThreshold;
+ customPropertyPrefix?: string | null;
+ followThrough?: SwipeFollowThrough | boolean;
+ allowFlick?: boolean | ((ms: number, px: number) => boolean);
+ enabled?: boolean;
+}
+```
+
+### Direction
+
+`SwipeableConfig` accepts an optional `direction` property that takes one or an array of directions for `swipeable` to register at runtime. Allow values are:
+
+```typescript title="SwipeDirection.d.ts"
+type SwipeSingleDirection = 'up' | 'down' | 'left' | 'right';
+type SwipeMultiDirection = 'x' | 'y' | 'all';
+type SwipeDirection = SwipeSingleDirection | SwipeMultiDirection;
+```
+
+The default is `['left', 'right']`. Although it is possible to allow `swpieable` to listen to all directions, it is recommended to constraint to either horizontal or vertical directions to avoid janky behavior.
+
+### Threshold
+
+`SwipeableConfig`accepts an optional `threshold` property that sets up the distance to trigger the `swipeend` event. The value is a string that takes a number followed by a unit (`px`, `rem`, `%`).
+
+```typescript title="SwipeThreshold.d.ts"
+type SwipeThresholdUnit = 'px' | 'rem' | '%';
+type SwipeThreshold = `${number}${SwipeThresholdUnit}`;
+```
+
+The default is `30%`. Note that percentage is relative to the element size in the travel direction (i.e height for vertical swipe, and width for horizontal swipe).
+
+### CSS Custom Property
+
+`SwipeableConfig` accepts an optional `customPropertyPrefix` property that sets up the CSS custom property to track the swipe travel distance. Typically you would use this property to shift the element's position following the swipe movement for visual feedback (so that user knows that their swipe is being registered).
+
+```svelte
+
+```
+
+
+
+`swipeable` tracks displacement via a custom property instead of setting the element's `style` directly to avoid interfering with user-defined styles, allowing more flexibility.
+
+
+
+
+The default is `--swipe`, i.e `--swipe-distance-x` for horizontal swipe, and `--swipe-distance-y` for vertical swipe. Set to `null` to disable tracking.
+
+### Follow Through
+
+`SwipeableConfig` accepts an optional `followThrough` property that instructs `swipeable` how to behave when swipe reaches the threshold:
+
+1. "Follow through" in the swipe direction, then fire `swipeend` event (as seen in [Demo]). That is, upon `pointerup`, the [CSS Custom Property] will be tweened to element's width / height.
+2. Stop swipe action immediately and fire `swipeend` event.
+
+`followThrough` takes either a boolean or a config object with the following interface.
+
+```typescript title="SwipeFollowThrough.d.ts"
+type SwipeFollowThrough = {
+ /** duration for the follow through animation */
+ duration?: number;
+ /** easing function for the follow through animation */
+ easing?: (t: number) => number;
+}
+```
+
+The default `true`, i.e (1). Set to `false` for (2).
+
+### Flick
+
+It is typical to expect a quick swipe with high velocity ("flick") to bypass the threshold and be recognized as a complete swipe action (try this in [Demo]). This can be configured via the `SwipeableConfig.allowFlick`.
+
+```typescript title="SwipeableConfig.allowFlick"
+interface SwipeableConfig {
+ // ... truncated ...
+ allowFlick?: boolean | ((ms: number, px: number) => boolean);
+}
+```
+
+For complex configuration, you can provide a function that takes the duration in milliseconds and the distance in pixels, and returns a boolean to indicate whether the swipe should be considered a flick. The default is:
+
+```typescript title="default flick check"
+// is flick if the duration is less than 170ms and the velocity is greater than 1px/ms
+const DEFAULT_FLICK_CHECK = (ms, px) => ms < 170 && Math.abs(px / ms) > 1;
+```
+
+---
+
+Happy swiping!
+
+[CustomEvent]: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
+[Demo]: #demo
+[CSS Custom Property]: #css-custom-property
+
diff --git a/sites/docs/src/routes/docs/(package)/swipeable/+page.server.ts b/sites/docs/src/routes/docs/(package)/swipeable/+page.server.ts
new file mode 100644
index 00000000..ef0b1eba
--- /dev/null
+++ b/sites/docs/src/routes/docs/(package)/swipeable/+page.server.ts
@@ -0,0 +1,16 @@
+import ogImage from '$lib/assets/images/og/svelte-put-swipeable.jpg';
+
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async ({ parent }) => {
+ const data = await parent();
+ return {
+ meta: {
+ ...data.meta,
+ og: {
+ image: ogImage,
+ }
+ },
+ };
+}
+
diff --git a/sites/docs/src/routes/docs/(package)/swipeable/_page/examples/demo.svelte b/sites/docs/src/routes/docs/(package)/swipeable/_page/examples/demo.svelte
new file mode 100644
index 00000000..7855e990
--- /dev/null
+++ b/sites/docs/src/routes/docs/(package)/swipeable/_page/examples/demo.svelte
@@ -0,0 +1,96 @@
+
+
+