scroll-arrows 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dan
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 ADDED
@@ -0,0 +1,155 @@
1
+ # scroll-arrows
2
+
3
+ [![CI](https://github.com/dancj/scroll-arrows/actions/workflows/ci.yml/badge.svg)](https://github.com/dancj/scroll-arrows/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/scroll-arrows.svg)](https://www.npmjs.com/package/scroll-arrows)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/scroll-arrows)](https://bundlephobia.com/package/scroll-arrows)
6
+ [![license](https://img.shields.io/npm/l/scroll-arrows.svg)](./LICENSE)
7
+
8
+ Hand-drawn arrows that **draw themselves between two elements as you scroll**.
9
+ A single `roughness` knob slides from clean straight lines (0) to scratchy,
10
+ curvy scribbles (1) — same sketchy engine as Excalidraw ([rough.js]).
11
+
12
+ Framework-agnostic core + a thin React wrapper. Arrows live in a click-through
13
+ overlay `<svg>`, auto-track their endpoints with `ResizeObserver`, and draw on
14
+ scroll progress.
15
+
16
+ ```bash
17
+ npm install scroll-arrows
18
+ ```
19
+
20
+ ## Vanilla
21
+
22
+ ```ts
23
+ import { scrollArrow } from "scroll-arrows";
24
+
25
+ const arrow = scrollArrow({
26
+ start: "#box-a", // Element or CSS selector
27
+ end: "#box-b",
28
+ roughness: 0.7, // 0 clean → 1 scratchy
29
+ stroke: "#e7e9ee",
30
+ strokeWidth: 2.5,
31
+ head: "end", // "start" | "end" | "both" | "none"
32
+ });
33
+
34
+ // later
35
+ arrow.destroy();
36
+ ```
37
+
38
+ ## How it works
39
+
40
+ - **Anchoring** — pass two elements; the arrow picks the best edges (`auto`
41
+ sockets) and recomputes when they move or resize. Override with
42
+ `startSocket` / `endSocket`.
43
+ - **Scroll draw** — progress is driven by a target's travel through the
44
+ viewport (`scroll.range`, fractions of viewport height, default `[0.85, 0.35]`).
45
+ `speed` finishes the stroke earlier/later; `easing` shapes the curve.
46
+ - **Roughness** — one knob mapped onto rough.js `roughness`/`bowing` plus path
47
+ curvature. `seed` keeps a given arrow's scribble stable across renders.
48
+ Endpoints stay pinned to the anchors at any roughness (`anchorEnds`, default
49
+ true); set it false to let scratchy ends wander off the targets.
50
+ - **Obstacle routing** — pass `avoid` (an element or array) and the curve bows
51
+ around them with an `avoidPadding` gap instead of cutting through. Single-bend
52
+ router: it clears the worst blocker, not a full path-finder.
53
+ - **Manual mode** — `scroll: false` + `setProgress(0..1)` to drive it yourself
54
+ (e.g. from GSAP/Motion).
55
+ - **Labels** — `label` rides along the line at `labelAt` (0..1, default mid)
56
+ and can sit off the line via `labelOffset` (perpendicular px; + = left of the
57
+ draw direction, − = right). Fades in as the pen draws through it.
58
+ `labelBackground` masks a gap in the line behind the text (the excalidraw
59
+ look); style via `labelColor` / `font`.
60
+
61
+ ## React
62
+
63
+ ```tsx
64
+ import { useRef } from "react";
65
+ import { ScrollArrowLine } from "scroll-arrows/react";
66
+
67
+ function Diagram() {
68
+ const a = useRef<HTMLDivElement>(null);
69
+ const b = useRef<HTMLDivElement>(null);
70
+ return (
71
+ <>
72
+ <div ref={a}>A</div>
73
+ <div ref={b}>B</div>
74
+ <ScrollArrowLine start={a} end={b} roughness={0.6} />
75
+ </>
76
+ );
77
+ }
78
+ ```
79
+
80
+ `ScrollArrowLine` renders nothing into the React tree — it manages the overlay
81
+ arrow via effect and cleans up on unmount. `useScrollArrow(opts)` is the hook
82
+ form. Pass `deps={[...]}` to re-create when inputs change.
83
+
84
+ ## Astro
85
+
86
+ The core is DOM-only, so run it in a client script (it must execute in the
87
+ browser, not at build time):
88
+
89
+ ```astro
90
+ ---
91
+ // Diagram.astro
92
+ ---
93
+ <div id="a">A</div>
94
+ <div id="b">B</div>
95
+
96
+ <script>
97
+ import { scrollArrow } from "scroll-arrows";
98
+ scrollArrow({ start: "#a", end: "#b", roughness: 0.6 });
99
+ </script>
100
+ ```
101
+
102
+ For an Astro React island, use the React API and hydrate with `client:visible`:
103
+
104
+ ```astro
105
+ <Diagram client:visible />
106
+ ```
107
+
108
+ ## API
109
+
110
+ `scrollArrow(options)` / `new ScrollArrow(options)` → instance with
111
+ `setProgress(p)`, `refresh()`, `destroy()`.
112
+
113
+ Key options: `start`, `end`, `container`, `roughness`, `stroke`, `strokeWidth`,
114
+ `seed`, `startSocket`, `endSocket`, `curvature`, `head`, `headSize`, `scroll`,
115
+ `speed`, `easing`, `progress`. Full types ship with the package.
116
+
117
+ ## Develop
118
+
119
+ ```bash
120
+ npm run demo # vite playground at /demo
121
+ npm test # vitest (library + release tooling)
122
+ npm run coverage # vitest + v8 coverage (pure logic gated at 90%)
123
+ npm run build # tsup → dist (ESM + CJS + d.ts)
124
+ ```
125
+
126
+ ## Releasing
127
+
128
+ Automated **staging → main** flow (semver, derived from conventional-commit PR
129
+ titles):
130
+
131
+ 1. Feature PRs merge into `staging`.
132
+ 2. `auto-release-pr.yml` keeps a single **"Release: staging to main"** PR open,
133
+ its body a categorized summary (Features / Fixes / Docs / Maintenance) plus
134
+ the proposed next version (`feat:` → minor, `fix:`/other → patch, `!` or
135
+ `BREAKING CHANGE` → major).
136
+ 3. Merging that PR to `main` triggers `release-changelog.yml`: it computes the
137
+ version from the newest `v*` tag, bumps `package.json` + prepends a
138
+ `CHANGELOG.md` entry on a `release-<version>` branch, tags `v<version>`, and
139
+ opens a sync PR back to `staging`.
140
+ 4. The pushed `v*` tag triggers `release.yml`, publishing to npm via
141
+ **trusted publishing (OIDC)** with provenance (version pinned to the tag).
142
+
143
+ Release logic lives in `scripts/*.mjs` (pure helpers + injectable-deps
144
+ orchestrators), unit-tested under vitest.
145
+
146
+ Publishing uses OIDC — no `NPM_TOKEN` secret. One-time bootstrap (npm requires
147
+ the package to exist before a trusted publisher can be configured):
148
+
149
+ 1. `npm login` then `npm publish --access public` once from your machine.
150
+ 2. On npmjs.com → the package → Settings → **Trusted Publishing**, add the
151
+ GitHub Actions publisher (`dancj/scroll-arrows`, workflow `release.yml`).
152
+
153
+ After that, every `v*` tag publishes from CI with no stored credentials.
154
+
155
+ [rough.js]: https://roughjs.com