react-smokey-fluid-cursor 1.0.3 β 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +269 -115
- package/dist/index.d.mts +301 -0
- package/dist/index.d.ts +249 -3
- package/dist/index.js +195 -292
- package/dist/index.mjs +194 -292
- package/package.json +68 -20
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Farasat Ali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,181 +1,335 @@
|
|
|
1
|
-
<h1 align="center">π¨ React Smokey Fluid Cursor</h1>
|
|
2
|
-
|
|
3
1
|
<p align="center">
|
|
4
|
-
|
|
2
|
+
<img src="https://raw.githubusercontent.com/faraasat/react-smokey-fluid-cursor/main/.github/assets/banner.svg" alt="react-smokey-fluid-cursor" width="100%" />
|
|
5
3
|
</p>
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[](https://standardjs.com)
|
|
5
|
+
<p align="center">
|
|
6
|
+
A GPU-accelerated fluid-simulation cursor trail for React and Next.js β one component, no configuration required.
|
|
7
|
+
</p>
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/react-smokey-fluid-cursor"><img alt="npm version" src="https://img.shields.io/npm/v/react-smokey-fluid-cursor?color=cb3837&label=npm&logo=npm"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/react-smokey-fluid-cursor"><img alt="downloads" src="https://img.shields.io/npm/dm/react-smokey-fluid-cursor?color=cb3837&label=downloads"></a>
|
|
12
|
+
<a href="https://bundlephobia.com/package/react-smokey-fluid-cursor"><img alt="bundle size" src="https://img.shields.io/bundlephobia/minzip/react-smokey-fluid-cursor?label=minzipped"></a>
|
|
13
|
+
<a href="https://github.com/faraasat/react-smokey-fluid-cursor/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/faraasat/react-smokey-fluid-cursor/actions/workflows/ci.yml/badge.svg"></a>
|
|
14
|
+
<img alt="types" src="https://img.shields.io/badge/types-included-3178c6?logo=typescript&logoColor=white">
|
|
15
|
+
<a href="https://github.com/faraasat/react-smokey-fluid-cursor/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/npm/l/react-smokey-fluid-cursor?color=blue"></a>
|
|
16
|
+
</p>
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://faraasat.github.io/react-smokey-fluid-cursor/"><b>Live demo</b></a> Β·
|
|
20
|
+
<a href="https://www.npmjs.com/package/react-smokey-fluid-cursor">npm</a> Β·
|
|
21
|
+
<a href="https://github.com/faraasat/react-smokey-fluid-cursor/blob/main/CHANGELOG.md">Changelog</a> Β·
|
|
22
|
+
<a href="https://github.com/faraasat/react-smokey-fluid-cursor/issues">Issues</a>
|
|
23
|
+
</p>
|
|
19
24
|
|
|
20
25
|
---
|
|
21
26
|
|
|
22
|
-
##
|
|
27
|
+
## Upgrading from 1.x
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
`2.0.0` adds scoped rendering, an imperative handle and live config updates.
|
|
30
|
+
`<SmokeyFluidCursor />` keeps its props, but four behaviours differ.
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
| Change | Impact | What to do |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| **The component renders `null`** when not `scoped` | The canvas is created and appended by the engine rather than returned from render, so it no longer appears where you placed the component | Nothing, unless you relied on its position in the DOM. Use `scoped` to keep it inside your own wrapper |
|
|
35
|
+
| **Placement is applied inline**, not via an injected `<style>` | CSS you wrote against `#smokey-fluid-canvas` no longer wins | Use the `position`, `zIndex`, `pointerEvents` and `className` options, or add `!important` |
|
|
36
|
+
| **Device pixel ratio is capped at 2** (`maxDpr`) | Slightly softer on 3x displays, markedly better frame rate and battery | `maxDpr: Infinity` restores the old behaviour |
|
|
37
|
+
| **`prefers-reduced-motion` is honoured** | Visitors who asked for reduced motion get a still canvas | `respectReducedMotion: false` opts out |
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
### The effect is invisible after upgrading?
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
The canvas sits behind your content at `z-index: -9999`. If your page sets an
|
|
42
|
+
opaque background on `<body>`, it paints over the effect β see
|
|
43
|
+
[the section above](#the-effect-is-invisible-check-your-page-background). This
|
|
44
|
+
applied to 1.x too, but the package now warns about it in development.
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
### New, optional
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
```tsx
|
|
49
|
+
const fluid = useRef<FluidHandle>(null);
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
<SmokeyFluidCursor ref={fluid} scoped config={{ palette: ["#ff4ecd"] }} />;
|
|
52
|
+
fluid.current?.pause();
|
|
53
|
+
fluid.current?.setConfig({ curl: 30 });
|
|
54
|
+
```
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
## Why
|
|
41
57
|
|
|
42
|
-
|
|
58
|
+
A real-time NavierβStokes fluid solver running in WebGL, wired to your pointer
|
|
59
|
+
and wrapped as a single React component. It cleans up after itself on unmount,
|
|
60
|
+
survives React StrictMode's double-invoke, and degrades quietly on devices
|
|
61
|
+
without WebGL instead of crashing your page.
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
> Not using React? See
|
|
64
|
+
> [`smokey-fluid-cursor`](https://github.com/faraasat/smokey-fluid-cursor).
|
|
45
65
|
|
|
46
|
-
|
|
66
|
+
## Installation
|
|
47
67
|
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
68
|
+
```bash
|
|
69
|
+
npm install react-smokey-fluid-cursor
|
|
70
|
+
```
|
|
51
71
|
|
|
52
|
-
|
|
72
|
+
<details>
|
|
73
|
+
<summary>yarn / pnpm / bun</summary>
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<SmokeyFluidCursor />
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
75
|
+
```bash
|
|
76
|
+
yarn add react-smokey-fluid-cursor
|
|
77
|
+
pnpm add react-smokey-fluid-cursor
|
|
78
|
+
bun add react-smokey-fluid-cursor
|
|
62
79
|
```
|
|
80
|
+
</details>
|
|
63
81
|
|
|
64
|
-
|
|
82
|
+
**Peer dependencies:** `react >= 17`, `react-dom >= 17`.
|
|
65
83
|
|
|
66
|
-
|
|
67
|
-
// src/main.tsx|jsx
|
|
68
|
-
import React from "react";
|
|
84
|
+
## Quick start
|
|
69
85
|
|
|
86
|
+
```tsx
|
|
70
87
|
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
71
88
|
|
|
72
|
-
function
|
|
89
|
+
export default function Layout({ children }) {
|
|
73
90
|
return (
|
|
74
91
|
<>
|
|
75
92
|
<SmokeyFluidCursor />
|
|
93
|
+
{children}
|
|
76
94
|
</>
|
|
77
95
|
);
|
|
78
96
|
}
|
|
79
|
-
|
|
80
|
-
export default Main;
|
|
81
97
|
```
|
|
82
98
|
|
|
83
|
-
|
|
99
|
+
That is the whole integration. The component renders nothing itself β it
|
|
100
|
+
creates a `fixed`, full-viewport canvas behind your content with
|
|
101
|
+
`pointer-events: none`, and tears it down on unmount.
|
|
102
|
+
|
|
103
|
+
> **Next.js App Router:** the package ships the `"use client"` directive, so it
|
|
104
|
+
> can be imported straight into a server component.
|
|
105
|
+
|
|
106
|
+
## Scope it to one section
|
|
107
|
+
|
|
108
|
+
Pass `scoped` and an `absolute` position, and the effect stays inside the
|
|
109
|
+
component's own wrapper instead of covering the page:
|
|
84
110
|
|
|
85
111
|
```tsx
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
<SmokeyFluidCursor
|
|
113
|
+
scoped
|
|
114
|
+
style={{ height: 320, borderRadius: 16 }}
|
|
115
|
+
config={{ position: "absolute", zIndex: 0, palette: ["#4ea8ff", "#7c4dff"] }}
|
|
116
|
+
>
|
|
117
|
+
<h2>Hover me</h2>
|
|
118
|
+
</SmokeyFluidCursor>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
When `scoped`, the wrapper gets `position: relative` and `overflow: hidden`
|
|
122
|
+
automatically, so the fluid is clipped to it.
|
|
123
|
+
|
|
124
|
+
## Controlling it
|
|
125
|
+
|
|
126
|
+
Grab a ref for a live handle β pause, resume or retune without remounting:
|
|
88
127
|
|
|
128
|
+
```tsx
|
|
129
|
+
import { useRef } from "react";
|
|
89
130
|
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
131
|
+
import type { FluidHandle } from "react-smokey-fluid-cursor";
|
|
132
|
+
|
|
133
|
+
function Page() {
|
|
134
|
+
const fluid = useRef<FluidHandle>(null);
|
|
90
135
|
|
|
91
|
-
function MyApp({ Component, pageProps }: AppProps) {
|
|
92
136
|
return (
|
|
93
137
|
<>
|
|
94
|
-
{
|
|
95
|
-
<
|
|
96
|
-
<
|
|
138
|
+
<SmokeyFluidCursor ref={fluid} />
|
|
139
|
+
<button onClick={() => fluid.current?.pause()}>Pause</button>
|
|
140
|
+
<button onClick={() => fluid.current?.setConfig({ curl: 30 })}>Swirl</button>
|
|
97
141
|
</>
|
|
98
142
|
);
|
|
99
143
|
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
| Method | Description |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `pause()` / `resume()` | Freeze or restart the simulation. |
|
|
149
|
+
| `isPaused()` | Current state. |
|
|
150
|
+
| `setConfig(partial)` | Retune in place, no remount. |
|
|
151
|
+
| `splat(x, y, color?)` | Inject a splash, in CSS pixels relative to the canvas. |
|
|
152
|
+
| `dispose()` | Tear down early (the component already does this on unmount). |
|
|
153
|
+
| `canvas` | The canvas being rendered into. |
|
|
154
|
+
|
|
155
|
+
### The hook
|
|
100
156
|
|
|
101
|
-
|
|
157
|
+
For full control over where the effect lives:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { useSmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
161
|
+
|
|
162
|
+
function Hero() {
|
|
163
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
164
|
+
useSmokeyFluidCursor({ position: "absolute" }, ref);
|
|
165
|
+
return <div ref={ref} style={{ position: "relative", height: 300 }} />;
|
|
166
|
+
}
|
|
102
167
|
```
|
|
103
168
|
|
|
104
|
-
|
|
169
|
+
## Updating config
|
|
170
|
+
|
|
171
|
+
Cheap options (`curl`, `splatForce`, `palette`, `colorIntensity`, dissipation,
|
|
172
|
+
`paused`, `zIndex`, β¦) are pushed straight into the running simulation.
|
|
173
|
+
|
|
174
|
+
Structural options (`id`, `position`, `simResolution`, `dyeResolution`) rebuild
|
|
175
|
+
it, because they reallocate buffers or move DOM. Changing those every render
|
|
176
|
+
would be expensive, so keep them stable.
|
|
177
|
+
|
|
178
|
+
## The effect is invisible? Check your page background
|
|
179
|
+
|
|
180
|
+
This is the single most common integration problem, and it looks like the
|
|
181
|
+
package is broken when it is not.
|
|
182
|
+
|
|
183
|
+
The canvas defaults to `z-index: -9999` so it sits behind your content. Per the
|
|
184
|
+
CSS painting order, a negatively-stacked element paints **above the root
|
|
185
|
+
background but below the background of block-level descendants** β so this
|
|
186
|
+
extremely common setup hides the effect completely:
|
|
187
|
+
|
|
188
|
+
```css
|
|
189
|
+
/* β body's background paints straight over the canvas */
|
|
190
|
+
body { background: #0b0f17; }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Put the page background on `<html>` instead:
|
|
194
|
+
|
|
195
|
+
```css
|
|
196
|
+
/* β the canvas paints above the root background, below your content */
|
|
197
|
+
html { background: #0b0f17; }
|
|
198
|
+
body { background: transparent; }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Alternatively, lift the canvas above your background and push your content
|
|
202
|
+
above the canvas:
|
|
105
203
|
|
|
106
204
|
```tsx
|
|
107
|
-
|
|
108
|
-
|
|
205
|
+
initFluid({ zIndex: 0 });
|
|
206
|
+
```
|
|
207
|
+
```css
|
|
208
|
+
main { position: relative; z-index: 1; }
|
|
209
|
+
```
|
|
109
210
|
|
|
110
|
-
|
|
211
|
+
In development the package detects this and warns in the console rather than
|
|
212
|
+
leaving you with a blank screen.
|
|
111
213
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
214
|
+
## Configuration
|
|
215
|
+
|
|
216
|
+
Every option is optional.
|
|
217
|
+
|
|
218
|
+
### Mounting & placement
|
|
219
|
+
|
|
220
|
+
| Option | Type | Default | Description |
|
|
221
|
+
| --- | --- | --- | --- |
|
|
222
|
+
| `container` | `HTMLElement \| string` | `document.body` | Where to create the canvas. Set automatically when `scoped`. |
|
|
223
|
+
| `canvas` | `HTMLCanvasElement \| string` | β | Render into an existing canvas instead. |
|
|
224
|
+
| `id` | `string` | `"smokey-fluid-canvas"` | Id assigned to the canvas. |
|
|
225
|
+
| `position` | `"fixed" \| "absolute" \| "relative" \| "static"` | `"fixed"` | `absolute` confines the effect to its container. |
|
|
226
|
+
| `zIndex` | `number` | `-9999` | Stacking order. |
|
|
227
|
+
| `pointerEvents` | `boolean` | `false` | Whether the canvas swallows clicks. |
|
|
228
|
+
| `className` | `string` | β | Extra class on the canvas. |
|
|
229
|
+
|
|
230
|
+
### Performance & accessibility
|
|
231
|
+
|
|
232
|
+
| Option | Type | Default | Description |
|
|
233
|
+
| --- | --- | --- | --- |
|
|
234
|
+
| `maxDpr` | `number` | `2` | Caps the device pixel ratio. Uncapped, a 3x phone renders **nine times** the pixels of a 1x display. |
|
|
235
|
+
| `pauseOnHidden` | `boolean` | `true` | Stop the loop while the tab is backgrounded. |
|
|
236
|
+
| `respectReducedMotion` | `boolean` | `true` | Start paused for `prefers-reduced-motion: reduce`. |
|
|
237
|
+
| `paused` | `boolean` | `false` | Start frozen. |
|
|
238
|
+
|
|
239
|
+
### Appearance
|
|
240
|
+
|
|
241
|
+
| Option | Type | Default | Description |
|
|
242
|
+
| --- | --- | --- | --- |
|
|
243
|
+
| `palette` | `string[]` | `null` | Hex colours to draw from, e.g. `["#ff4ecd"]`. Omit for random hues. |
|
|
244
|
+
| `colorIntensity` | `number` | `0.15` | Brightness multiplier. |
|
|
245
|
+
| `backColor` | `{ r, g, b }` | `{ r: 0, g: 0, b: 0 }` | Canvas background. |
|
|
246
|
+
| `transparent` | `boolean` | `true` | Blend with the page background. |
|
|
247
|
+
| `shading` | `boolean` | `true` | Lighting, for depth. |
|
|
248
|
+
| `colorUpdateSpeed` | `number` | `10` | How fast the palette rotates. |
|
|
249
|
+
|
|
250
|
+
### Simulation
|
|
251
|
+
|
|
252
|
+
| Option | Type | Default | Description |
|
|
253
|
+
| --- | --- | --- | --- |
|
|
254
|
+
| `simResolution` | `number` | `128` | Velocity/pressure grid. |
|
|
255
|
+
| `dyeResolution` | `number` | `1440` | Colour buffer resolution β the main quality/cost dial. |
|
|
256
|
+
| `densityDissipation` | `number` | `3.5` | How fast colour fades. |
|
|
257
|
+
| `velocityDissipation` | `number` | `2` | How fast motion slows. |
|
|
258
|
+
| `pressure` | `number` | `0.1` | Initial pressure multiplier. |
|
|
259
|
+
| `pressureIteration` | `number` | `20` | Jacobi iterations. |
|
|
260
|
+
| `curl` | `number` | `10` | Vorticity confinement β the swirliness. |
|
|
261
|
+
| `splatRadius` | `number` | `0.5` | Size of each splat. |
|
|
262
|
+
| `splatForce` | `number` | `6000` | Force per splat. |
|
|
263
|
+
|
|
264
|
+
## Lifecycle
|
|
265
|
+
|
|
266
|
+
On unmount the component stops the render loop, detaches its window listeners,
|
|
267
|
+
releases the WebGL context and removes the canvas it created. Mounting and
|
|
268
|
+
unmounting repeatedly β including StrictMode's development double-invoke β
|
|
269
|
+
does not stack simulations.
|
|
270
|
+
|
|
271
|
+
## Performance
|
|
272
|
+
|
|
273
|
+
The big levers, in order of impact:
|
|
274
|
+
|
|
275
|
+
1. **`maxDpr`** β already capped at `2`. Drop to `1` for the weakest devices.
|
|
276
|
+
2. **`dyeResolution`** β `512` is much cheaper and still looks good.
|
|
277
|
+
3. **`pressureIteration`** β `10` roughly halves the solver cost.
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
<SmokeyFluidCursor config={{ maxDpr: 1, dyeResolution: 512, pressureIteration: 10 }} />
|
|
126
281
|
```
|
|
127
282
|
|
|
128
|
-
|
|
283
|
+
## Accessibility
|
|
129
284
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| Property | Default Value | Description |
|
|
135
|
-
| --------------------- | ----------------------------- | ---------------------------------------------- |
|
|
136
|
-
| `id` | `"react-smokey-fluid-cursor"` | Canvas element ID |
|
|
137
|
-
| `simResolution` | `128` | Simulation resolution (higher = more detailed) |
|
|
138
|
-
| `dyeResolution` | `512` | Dye/color resolution |
|
|
139
|
-
| `densityDissipation` | `0.98` | How quickly colors fade (0β1) |
|
|
140
|
-
| `velocityDissipation` | `0.98` | How quickly movement slows down |
|
|
141
|
-
| `pressureIteration` | `10` | Pressure solver iterations |
|
|
142
|
-
| `curl` | `30` | Vorticity/swirl intensity |
|
|
143
|
-
| `splatRadius` | `0.25` | Size of cursor splats |
|
|
144
|
-
| `splatForce` | `6000` | Force of cursor movements |
|
|
145
|
-
| `shading` | `true` | Enable 3D lighting effects |
|
|
146
|
-
| `colorUpdateSpeed` | `0.5` | Speed of color transitions |
|
|
147
|
-
| `transparent` | `false` | Transparent background |
|
|
285
|
+
A full-screen animation is a real problem for people with vestibular
|
|
286
|
+
disorders. By default this honours `prefers-reduced-motion: reduce` by starting
|
|
287
|
+
paused, and reacts if the preference changes while the page is open.
|
|
148
288
|
|
|
149
|
-
|
|
289
|
+
## Browser support
|
|
150
290
|
|
|
151
|
-
|
|
291
|
+
Requires WebGL (WebGL 2 when available, WebGL 1 fallback). Without it the
|
|
292
|
+
component logs a warning and renders nothing β it never throws, so a decorative
|
|
293
|
+
effect cannot take down your app.
|
|
152
294
|
|
|
153
|
-
|
|
154
|
-
- **WebGL Accelerated**: High-performance rendering for smooth 60fps
|
|
155
|
-
- **Interactive**: Responds to mouse and touch movements
|
|
156
|
-
- **Customizable**: Extensive configuration options
|
|
157
|
-
- **Mobile Support**: Touch-optimized interactions
|
|
158
|
-
- **Auto-scaling**: Adapts to screen size and pixel ratio
|
|
159
|
-
- **Color Cycling**: Dynamic, evolving color palettes
|
|
160
|
-
- **3D Lighting**: Optional shading for depth perception
|
|
295
|
+
## Contributing
|
|
161
296
|
|
|
162
|
-
|
|
297
|
+
Issues and pull requests are welcome.
|
|
163
298
|
|
|
164
|
-
|
|
299
|
+
```bash
|
|
300
|
+
git clone https://github.com/faraasat/react-smokey-fluid-cursor.git
|
|
301
|
+
cd react-smokey-fluid-cursor
|
|
302
|
+
npm install
|
|
303
|
+
npm test # vitest unit tests
|
|
304
|
+
npm run typecheck # tsc --noEmit
|
|
305
|
+
npm run build # tsup
|
|
306
|
+
```
|
|
165
307
|
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
- **Art Installations**: Digital art and creative coding
|
|
170
|
-
- **Game Effects**: Atmospheric and UI effects
|
|
171
|
-
- **Product Demos**: Eye-catching technology showcases
|
|
308
|
+
End-to-end tests run against the built demo in a real browser (desktop and
|
|
309
|
+
mobile viewports), and cover the things unit tests cannot: layout, CSS and
|
|
310
|
+
keyboard behaviour.
|
|
172
311
|
|
|
173
|
-
|
|
312
|
+
```bash
|
|
313
|
+
npm run build && npm --prefix example install && npm --prefix example run build
|
|
314
|
+
npm run test:e2e # playwright
|
|
315
|
+
npm run test:e2e:ui # interactive
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
To run the demo site against your local build:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
npm run example:dev
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Releases are manual β nothing publishes on a push to `main`. Maintainers run
|
|
325
|
+
the **Release** workflow from the Actions tab.
|
|
326
|
+
|
|
327
|
+
## Privacy
|
|
174
328
|
|
|
175
|
-
|
|
329
|
+
The published package contains **no telemetry**. The demo site at
|
|
330
|
+
[faraasat.github.io/react-smokey-fluid-cursor](https://faraasat.github.io/react-smokey-fluid-cursor/) uses
|
|
331
|
+
Google Analytics and Aptabase; the library itself never phones home.
|
|
176
332
|
|
|
177
|
-
|
|
333
|
+
## License
|
|
178
334
|
|
|
179
|
-
|
|
180
|
-
- LinkedIn: [linkedin.com/in/faraasat](https://linkedin.com/in/faraasat)
|
|
181
|
-
- GitHub: [github.com/faraasat](https://github.com/faraasat)
|
|
335
|
+
[MIT](./LICENSE) Β© [Farasat Ali](https://github.com/faraasat)
|