situs-kit 0.2.0 → 0.2.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Surya Aditya
3
+ Copyright (c) 2026 Surya Aditya
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,19 +1,309 @@
1
- # situs-kit
1
+ # Situs Toolkit
2
2
 
3
3
  A zero-dependency creative developer toolkit
4
4
 
5
- ## Install
5
+ - [SplitText](#splittext)
6
+ - [SmoothScroll](#smoothscroll)
7
+ - [Ticker](#ticker)
6
8
 
7
- ```bash
8
- npm install situs-kit
9
+ ---
10
+
11
+ # SplitText
12
+
13
+ Split text into characters, words, and lines while preserving inline HTML elements like `<a>`, `<em>`, `<strong>`, and `<u>`.
14
+
15
+ ## Basic Usage
16
+
17
+ ```ts
18
+ import { SplitText } from "situs-kit/split-text";
19
+
20
+ const split = new SplitText("#my-text", {
21
+ type: ["chars", "words", "lines"],
22
+ });
23
+
24
+ split.chars; // HTMLElement[]
25
+ split.words; // HTMLElement[]
26
+ split.lines; // HTMLElement[]
27
+ ```
28
+
29
+ ## Constructor
30
+
31
+ ```ts
32
+ new SplitText(
33
+ element: HTMLElement | HTMLElement[] | ArrayLike<HTMLElement> | string,
34
+ options?: SplitTextOptions
35
+ )
36
+ ```
37
+
38
+ The `element` parameter accepts:
39
+
40
+ - A CSS selector string (e.g. `"#my-text"`, `".my-class"`)
41
+ - A single `HTMLElement`
42
+ - An array or array-like collection of `HTMLElement`s (creates sub-instances, aggregating results)
43
+
44
+ ## Options
45
+
46
+ ```ts
47
+ new SplitText(element, {
48
+ type: ["chars", "words", "lines"], // which levels to split
49
+ tag: "span", // wrapper element tag
50
+ mask: ["chars", "lines"], // which levels to mask
51
+ resize: true, // auto-reflow lines on resize
52
+ style: {
53
+ chars: { color: "red" },
54
+ words: {},
55
+ lines: {},
56
+ mask: {},
57
+ },
58
+ class: {
59
+ chars: "my-char",
60
+ words: "my-word",
61
+ lines: "my-line",
62
+ mask: "my-mask",
63
+ },
64
+ });
65
+ ```
66
+
67
+ | Option | Type | Default | Description |
68
+ |--------|------|---------|-------------|
69
+ | `type` | `SplitType \| SplitType[]` | `["chars", "words", "lines"]` | Split granularity |
70
+ | `tag` | `string` | `"span"` | Wrapper element tag |
71
+ | `mask` | `boolean \| SplitType[]` | `false` | Create overflow-hidden wrappers. `true` masks all requested types; array masks only specified types |
72
+ | `resize` | `boolean` | `true` | Auto-reflow lines on window resize (only applies when lines are split) |
73
+ | `style` | `object` | — | Inline styles per split type (`chars`, `words`, `lines`, `mask`) |
74
+ | `class` | `object` | — | CSS classes per split type (`chars`, `words`, `lines`, `mask`) |
75
+
76
+ ### Types
77
+
78
+ ```ts
79
+ type SplitType = "chars" | "words" | "lines"
80
+
81
+ type SplitTypeOption =
82
+ | SplitType
83
+ | ["chars"]
84
+ | ["words"]
85
+ | ["lines"]
86
+ | ["chars", "words"]
87
+ | ["chars", "lines"]
88
+ | ["words", "lines"]
89
+ | ["chars", "words", "lines"]
90
+ ```
91
+
92
+ ## Properties
93
+
94
+ | Property | Type | Description |
95
+ |----------|------|-------------|
96
+ | `dom` | `HTMLElement \| HTMLElement[]` | The original element(s) |
97
+ | `chars` | `HTMLElement[]` | Character wrapper elements |
98
+ | `words` | `HTMLElement[]` | Word wrapper elements |
99
+ | `lines` | `HTMLElement[]` | Line wrapper elements |
100
+ | `masks` | `{ chars: HTMLElement[], words: HTMLElement[], lines: HTMLElement[] }` | Mask wrapper elements per split type |
101
+
102
+ ## Methods
103
+
104
+ ### `reflow()`
105
+
106
+ Recalculate line groupings without re-splitting characters or words. Called automatically on resize when `resize: true`. Does nothing if lines weren't requested in `type`.
107
+
108
+ ```ts
109
+ split.reflow();
9
110
  ```
10
111
 
11
- ## Modules
112
+ ### `revert()`
113
+
114
+ Restore the element to its original HTML. Clears all `chars`, `words`, `lines`, and `masks` arrays. Calls `destroy()` internally.
115
+
116
+ ```ts
117
+ split.revert();
118
+ ```
119
+
120
+ ### `destroy()`
121
+
122
+ Remove resize observers and cancel pending animation frames. Called automatically by `revert()`. Safe to call multiple times.
123
+
124
+ ```ts
125
+ split.destroy();
126
+ ```
127
+
128
+ ## Data Attributes
129
+
130
+ Split elements include data attributes for CSS targeting:
131
+
132
+ ```css
133
+ [data-char] { display: inline-block; }
134
+ [data-word] { display: inline-block; }
135
+ [data-line] { display: block; }
136
+ ```
137
+
138
+ Mask elements use:
139
+
140
+ - `data-maskChar`
141
+ - `data-maskWord`
142
+ - `data-maskLine`
143
+
144
+ ## HTML Preservation
145
+
146
+ Inline elements like `<a>`, `<em>`, `<strong>`, `<u>`, `<i>`, `<b>`, `<ins>`, `<s>`, `<strike>`, and `<del>` are preserved and cloned around split elements.
147
+
148
+ Opaque elements like `<div>`, `<span>`, `<svg>`, `<img>`, `<br>`, `<canvas>`, `<video>`, `<audio>`, `<iframe>`, `<input>`, `<textarea>`, `<select>`, `<button>`, `<picture>`, and `<figure>` are treated as atomic units.
149
+
150
+ ```html
151
+ <!-- Input -->
152
+ <p id="text">Hello <strong>world</strong></p>
153
+
154
+ <!-- After split, <strong> wraps are preserved -->
155
+ ```
156
+
157
+ ## Masking
158
+
159
+ Masks wrap split elements in `overflow: hidden` containers, useful for reveal animations. You can specify which split levels to mask:
160
+
161
+ ```ts
162
+ // mask only lines
163
+ const split = new SplitText("#text", {
164
+ type: ["words", "lines"],
165
+ mask: ["lines"],
166
+ });
167
+
168
+ split.masks.lines; // HTMLElement[] - overflow:hidden wrappers
169
+
170
+ // mask all split types
171
+ const split2 = new SplitText("#text", {
172
+ type: ["chars", "words", "lines"],
173
+ mask: true,
174
+ });
175
+ ```
176
+
177
+ ## Multi-Element Support
178
+
179
+ When passing multiple elements (via selector, array, or array-like), SplitText creates sub-instances and aggregates all results into the top-level `chars`, `words`, `lines`, and `masks` arrays.
180
+
181
+ ```ts
182
+ const split = new SplitText(".my-text");
183
+
184
+ // chars/words/lines contain elements from all matched elements
185
+ split.chars; // HTMLElement[] from all .my-text elements
186
+ ```
187
+
188
+ ## Additional Behaviors
189
+
190
+ - **Letter spacing**: Automatically applied to character elements when the parent has `letter-spacing` set
191
+ - **Text indent**: Preserved on the first line
192
+
193
+ ---
194
+
195
+ # SmoothScroll
196
+
197
+ Smooth scroll with frame-rate independent damping. Intercepts wheel for smooth interpolation, uses native scroll for touch/mobile.
198
+
199
+ ## Usage
200
+
201
+ ```ts
202
+ import { SmoothScroll } from "situs-kit/smooth-scroll";
203
+
204
+ const scroll = new SmoothScroll();
205
+ ```
206
+
207
+ ### Options
208
+
209
+ ```ts
210
+ const scroll = new SmoothScroll({
211
+ wrapper: "#scroll-box", // window | HTMLElement | string (default: window)
212
+ direction: "vertical", // "vertical" | "horizontal"
213
+ duration: 800, // global to() duration in ms (0 = use damp)
214
+ ease: 0.09, // number → damp factor, function → tween ease (default: 0.09)
215
+ prevent: true, // stop wheel propagation (default: true for elements, false for window)
216
+ });
217
+ ```
218
+
219
+ ## Scroll State
220
+
221
+ ```ts
222
+ const { current, target, velocity, direction, max, scrolling } = scroll.scroll;
223
+ ```
224
+
225
+ > **Note:** `scroll` returns a shared object that is mutated each frame. Destructure or copy the values if you need to compare across frames.
226
+
227
+ | Property | Type | Description |
228
+ |----------|------|-------------|
229
+ | `current` | `number` | Current interpolated position |
230
+ | `target` | `number` | Target position |
231
+ | `velocity` | `number` | Pixels moved since last frame |
232
+ | `direction` | `1 \| -1` | 1 = forward, -1 = backward |
233
+ | `max` | `number` | Maximum scroll value |
234
+ | `scrolling` | `boolean` | Whether currently scrolling |
235
+
236
+ ## Methods
237
+
238
+ ### `to(target, options?)`
239
+
240
+ ```ts
241
+ scroll.to(500);
242
+ scroll.to("#section-2", { offset: -100 });
243
+ scroll.to(element, { duration: 1000 });
244
+ scroll.to(0, { immediate: true });
245
+ ```
246
+
247
+ | Option | Type | Description |
248
+ |--------|------|-------------|
249
+ | `offset` | `number` | Pixel offset added to target |
250
+ | `duration` | `number` | Animation duration in ms (defaults to constructor `duration`) |
251
+ | `ease` | `number \| (t: number) => number` | Number → damp factor, function → tween ease curve |
252
+ | `immediate` | `boolean` | Jump instantly |
253
+ | `onComplete` | `() => void` | Callback when scroll completes |
254
+
255
+ ### `start()` / `stop()`
256
+
257
+ Resume or pause scroll listening and the animation loop.
258
+
259
+ ### `destroy()`
260
+
261
+ Full cleanup — removes listeners, stops the ticker, clears events.
262
+
263
+ ## Events
264
+
265
+ ```ts
266
+ scroll.on("scroll", ({ current, target, velocity, direction, max }) => {});
267
+ scroll.off("scroll", callback);
268
+ ```
269
+
270
+ ## Window vs Element Mode
271
+
272
+ Both modes use the same scroll-jacking approach: wheel is intercepted for smooth damping, touch and programmatic scrolls sync via native `scroll` events.
273
+
274
+ **Window** (default) — drives scroll via `window.scrollTo()`.
275
+
276
+ **Element** — pass a `wrapper` to enable. Drives scroll via `scrollTop`/`scrollLeft`. The wrapper element should have `overflow: auto` or `overflow: scroll`.
277
+
278
+ ---
279
+
280
+ # Ticker
281
+
282
+ Shared singleton RAF loop. Used internally by SmoothScroll.
283
+
284
+ ## Usage
285
+
286
+ ```ts
287
+ import { Ticker, getDeltaFrame } from "situs-kit/ticker";
288
+
289
+ const off = Ticker.add((elapsed) => {
290
+ const df = getDeltaFrame(); // 1.0 = 60fps frame
291
+ object.x += speed * df;
292
+ });
293
+
294
+ off(); // unsubscribe
295
+ ```
296
+
297
+ ## API
298
+
299
+ ### `Ticker.add(callback): () => void`
300
+
301
+ Add a callback to the loop. Returns an unsubscribe function. The callback receives `elapsed` (ms since added).
302
+
303
+ ### `Ticker.remove(callback): void`
12
304
 
13
- - [SplitText](./docs/split-text.md) Split text into characters, words, and lines with HTML preservation
14
- - [SmoothScroll](./docs/smooth-scroll.md) — Lerp-based smooth scroll with native touch/mobile support
15
- - [Ticker](./docs/ticker.md) — Shared RAF loop manager for frame-synced animations
305
+ Remove a callback by reference.
16
306
 
17
- ## License
307
+ ### `getDeltaFrame(): number`
18
308
 
19
- MIT
309
+ Normalized delta frame. `1.0` at 60fps, `2.0` at 30fps, `0.5` at 120fps.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "situs-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A creative developer helper library",
5
5
  "license": "MIT",
6
6
  "module": "./dist/index.js",
@@ -26,8 +26,7 @@
26
26
  }
27
27
  },
28
28
  "files": [
29
- "dist",
30
- "docs"
29
+ "dist"
31
30
  ],
32
31
  "scripts": {
33
32
  "build": "bun build.ts && bunx tsc -p tsconfig.build.json",
@@ -1,82 +0,0 @@
1
- # SmoothScroll
2
-
3
- Smooth scroll with frame-rate independent damping. Intercepts wheel for smooth interpolation, uses native scroll for touch/mobile.
4
-
5
- ## Usage
6
-
7
- ```ts
8
- import { SmoothScroll } from "situs-kit/smooth-scroll";
9
-
10
- const scroll = new SmoothScroll();
11
- ```
12
-
13
- ### Options
14
-
15
- ```ts
16
- const scroll = new SmoothScroll({
17
- wrapper: "#scroll-box", // window | HTMLElement | string (default: window)
18
- direction: "vertical", // "vertical" | "horizontal"
19
- duration: 800, // global to() duration in ms (0 = use damp)
20
- ease: 0.09, // number → damp factor, function → tween ease (default: 0.09)
21
- prevent: true, // stop wheel propagation (default: true for elements, false for window)
22
- });
23
- ```
24
-
25
- ## Scroll State
26
-
27
- ```ts
28
- const { current, target, velocity, direction, max, scrolling } = scroll.scroll;
29
- ```
30
-
31
- > **Note:** `scroll` returns a shared object that is mutated each frame. Destructure or copy the values if you need to compare across frames.
32
-
33
- | Property | Type | Description |
34
- |----------|------|-------------|
35
- | `current` | `number` | Current interpolated position |
36
- | `target` | `number` | Target position |
37
- | `velocity` | `number` | Pixels moved since last frame |
38
- | `direction` | `1 \| -1` | 1 = forward, -1 = backward |
39
- | `max` | `number` | Maximum scroll value |
40
- | `scrolling` | `boolean` | Whether currently scrolling |
41
-
42
- ## Methods
43
-
44
- ### `to(target, options?)`
45
-
46
- ```ts
47
- scroll.to(500);
48
- scroll.to("#section-2", { offset: -100 });
49
- scroll.to(element, { duration: 1000 });
50
- scroll.to(0, { immediate: true });
51
- ```
52
-
53
- | Option | Type | Description |
54
- |--------|------|-------------|
55
- | `offset` | `number` | Pixel offset added to target |
56
- | `duration` | `number` | Animation duration in ms (defaults to constructor `duration`) |
57
- | `ease` | `number \| (t: number) => number` | Number → damp factor, function → tween ease curve |
58
- | `immediate` | `boolean` | Jump instantly |
59
- | `onComplete` | `() => void` | Callback when scroll completes |
60
-
61
- ### `start()` / `stop()`
62
-
63
- Resume or pause scroll listening and the animation loop.
64
-
65
- ### `destroy()`
66
-
67
- Full cleanup — removes listeners, stops the ticker, clears events.
68
-
69
- ## Events
70
-
71
- ```ts
72
- scroll.on("scroll", ({ current, target, velocity, direction, max }) => {});
73
- scroll.off("scroll", callback);
74
- ```
75
-
76
- ## Window vs Element Mode
77
-
78
- Both modes use the same scroll-jacking approach: wheel is intercepted for smooth damping, touch and programmatic scrolls sync via native `scroll` events.
79
-
80
- **Window** (default) — drives scroll via `window.scrollTo()`.
81
-
82
- **Element** — pass a `wrapper` to enable. Drives scroll via `scrollTop`/`scrollLeft`. The wrapper element should have `overflow: auto` or `overflow: scroll`.
@@ -1,181 +0,0 @@
1
- # SplitText
2
-
3
- Split text into characters, words, and lines while preserving inline HTML elements like `<a>`, `<em>`, `<strong>`, and `<u>`.
4
-
5
- ## Basic Usage
6
-
7
- ```ts
8
- import { SplitText } from "situs-kit/split-text";
9
-
10
- const split = new SplitText("#my-text", {
11
- type: ["chars", "words", "lines"],
12
- });
13
-
14
- split.chars; // HTMLElement[]
15
- split.words; // HTMLElement[]
16
- split.lines; // HTMLElement[]
17
- ```
18
-
19
- ## Constructor
20
-
21
- ```ts
22
- new SplitText(
23
- element: HTMLElement | HTMLElement[] | ArrayLike<HTMLElement> | string,
24
- options?: SplitTextOptions
25
- )
26
- ```
27
-
28
- The `element` parameter accepts:
29
-
30
- - A CSS selector string (e.g. `"#my-text"`, `".my-class"`)
31
- - A single `HTMLElement`
32
- - An array or array-like collection of `HTMLElement`s (creates sub-instances, aggregating results)
33
-
34
- ## Options
35
-
36
- ```ts
37
- new SplitText(element, {
38
- type: ["chars", "words", "lines"], // which levels to split
39
- tag: "span", // wrapper element tag
40
- mask: ["chars", "lines"], // which levels to mask
41
- resize: true, // auto-reflow lines on resize
42
- style: {
43
- chars: { color: "red" },
44
- words: {},
45
- lines: {},
46
- mask: {},
47
- },
48
- class: {
49
- chars: "my-char",
50
- words: "my-word",
51
- lines: "my-line",
52
- mask: "my-mask",
53
- },
54
- });
55
- ```
56
-
57
- | Option | Type | Default | Description |
58
- |--------|------|---------|-------------|
59
- | `type` | `SplitType \| SplitType[]` | `["chars", "words", "lines"]` | Split granularity |
60
- | `tag` | `string` | `"span"` | Wrapper element tag |
61
- | `mask` | `boolean \| SplitType[]` | `false` | Create overflow-hidden wrappers. `true` masks all requested types; array masks only specified types |
62
- | `resize` | `boolean` | `true` | Auto-reflow lines on window resize (only applies when lines are split) |
63
- | `style` | `object` | — | Inline styles per split type (`chars`, `words`, `lines`, `mask`) |
64
- | `class` | `object` | — | CSS classes per split type (`chars`, `words`, `lines`, `mask`) |
65
-
66
- ### Types
67
-
68
- ```ts
69
- type SplitType = "chars" | "words" | "lines"
70
-
71
- type SplitTypeOption =
72
- | SplitType
73
- | ["chars"]
74
- | ["words"]
75
- | ["lines"]
76
- | ["chars", "words"]
77
- | ["chars", "lines"]
78
- | ["words", "lines"]
79
- | ["chars", "words", "lines"]
80
- ```
81
-
82
- ## Properties
83
-
84
- | Property | Type | Description |
85
- |----------|------|-------------|
86
- | `dom` | `HTMLElement \| HTMLElement[]` | The original element(s) |
87
- | `chars` | `HTMLElement[]` | Character wrapper elements |
88
- | `words` | `HTMLElement[]` | Word wrapper elements |
89
- | `lines` | `HTMLElement[]` | Line wrapper elements |
90
- | `masks` | `{ chars: HTMLElement[], words: HTMLElement[], lines: HTMLElement[] }` | Mask wrapper elements per split type |
91
-
92
- ## Methods
93
-
94
- ### `reflow()`
95
-
96
- Recalculate line groupings without re-splitting characters or words. Called automatically on resize when `resize: true`. Does nothing if lines weren't requested in `type`.
97
-
98
- ```ts
99
- split.reflow();
100
- ```
101
-
102
- ### `revert()`
103
-
104
- Restore the element to its original HTML. Clears all `chars`, `words`, `lines`, and `masks` arrays. Calls `destroy()` internally.
105
-
106
- ```ts
107
- split.revert();
108
- ```
109
-
110
- ### `destroy()`
111
-
112
- Remove resize observers and cancel pending animation frames. Called automatically by `revert()`. Safe to call multiple times.
113
-
114
- ```ts
115
- split.destroy();
116
- ```
117
-
118
- ## Data Attributes
119
-
120
- Split elements include data attributes for CSS targeting:
121
-
122
- ```css
123
- [data-char] { display: inline-block; }
124
- [data-word] { display: inline-block; }
125
- [data-line] { display: block; }
126
- ```
127
-
128
- Mask elements use:
129
-
130
- - `data-maskChar`
131
- - `data-maskWord`
132
- - `data-maskLine`
133
-
134
- ## HTML Preservation
135
-
136
- Inline elements like `<a>`, `<em>`, `<strong>`, `<u>`, `<i>`, `<b>`, `<ins>`, `<s>`, `<strike>`, and `<del>` are preserved and cloned around split elements.
137
-
138
- Opaque elements like `<div>`, `<span>`, `<svg>`, `<img>`, `<br>`, `<canvas>`, `<video>`, `<audio>`, `<iframe>`, `<input>`, `<textarea>`, `<select>`, `<button>`, `<picture>`, and `<figure>` are treated as atomic units.
139
-
140
- ```html
141
- <!-- Input -->
142
- <p id="text">Hello <strong>world</strong></p>
143
-
144
- <!-- After split, <strong> wraps are preserved -->
145
- ```
146
-
147
- ## Masking
148
-
149
- Masks wrap split elements in `overflow: hidden` containers, useful for reveal animations. You can specify which split levels to mask:
150
-
151
- ```ts
152
- // mask only lines
153
- const split = new SplitText("#text", {
154
- type: ["words", "lines"],
155
- mask: ["lines"],
156
- });
157
-
158
- split.masks.lines; // HTMLElement[] - overflow:hidden wrappers
159
-
160
- // mask all split types
161
- const split2 = new SplitText("#text", {
162
- type: ["chars", "words", "lines"],
163
- mask: true,
164
- });
165
- ```
166
-
167
- ## Multi-Element Support
168
-
169
- When passing multiple elements (via selector, array, or array-like), SplitText creates sub-instances and aggregates all results into the top-level `chars`, `words`, `lines`, and `masks` arrays.
170
-
171
- ```ts
172
- const split = new SplitText(".my-text");
173
-
174
- // chars/words/lines contain elements from all matched elements
175
- split.chars; // HTMLElement[] from all .my-text elements
176
- ```
177
-
178
- ## Additional Behaviors
179
-
180
- - **Letter spacing**: Automatically applied to character elements when the parent has `letter-spacing` set
181
- - **Text indent**: Preserved on the first line
package/docs/ticker.md DELETED
@@ -1,30 +0,0 @@
1
- # Ticker
2
-
3
- Shared singleton RAF loop. Used internally by SmoothScroll.
4
-
5
- ## Usage
6
-
7
- ```ts
8
- import { Ticker, getDeltaFrame } from "situs-kit/ticker";
9
-
10
- const off = Ticker.add((elapsed) => {
11
- const df = getDeltaFrame(); // 1.0 = 60fps frame
12
- object.x += speed * df;
13
- });
14
-
15
- off(); // unsubscribe
16
- ```
17
-
18
- ## API
19
-
20
- ### `Ticker.add(callback): () => void`
21
-
22
- Add a callback to the loop. Returns an unsubscribe function. The callback receives `elapsed` (ms since added).
23
-
24
- ### `Ticker.remove(callback): void`
25
-
26
- Remove a callback by reference.
27
-
28
- ### `getDeltaFrame(): number`
29
-
30
- Normalized delta frame. `1.0` at 60fps, `2.0` at 30fps, `0.5` at 120fps.