@brandonsoccer22/gsap-editorial-carousel 0.1.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/package.json +47 -0
  4. package/styles.css +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brandon Joyce
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,198 @@
1
+ # @brandonsoccer22/gsap-carousel
2
+
3
+ A small, class-driven GSAP carousel controller that handles state, accessibility, animation orchestration, and dot cloning without enforcing layout. You own the markup and CSS; the library handles wiring.
4
+
5
+ ## Why class-driven animations?
6
+ Content editors can add animation classes and data attributes directly in a CMS without touching JavaScript. Elements inside slides opt into motion by adding a class that matches a registered animation.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @brandonsoccer22/gsap-carousel gsap
12
+ ```
13
+
14
+ ## Markup (semantic + user-placed controls)
15
+
16
+ ```html
17
+ <section data-carousel>
18
+ <div class="hero">
19
+ <button data-carousel-prev>Prev</button>
20
+ <button data-carousel-next>Next</button>
21
+ </div>
22
+
23
+ <div class="slides">
24
+ <article data-carousel-slide>
25
+ <h2 class="fade-up" data-seq="1">Headline</h2>
26
+ <p class="fade-in" data-seq="2" data-dur="0.4">Description</p>
27
+ </article>
28
+
29
+ <article data-carousel-slide>
30
+ <h2 class="slide-in" data-seq="1">Second</h2>
31
+ <p class="fade-in" data-seq="2">More copy</p>
32
+ </article>
33
+ </div>
34
+
35
+ <div data-carousel-dots>
36
+ <button data-carousel-dot-template>•</button>
37
+ </div>
38
+ </section>
39
+ ```
40
+
41
+ - The dot template is cloned once per slide and hidden via `template.hidden = true`.
42
+ - Controls can be placed anywhere inside the carousel markup; no wrappers are injected.
43
+ - Re-initialization clears previously generated dots (`data-carousel-dot=\"true\"`) before cloning.
44
+
45
+ ## Usage
46
+
47
+ ```ts
48
+ import "@brandonsoccer22/gsap-carousel/styles.css";
49
+ import { createCarousel } from "@brandonsoccer22/gsap-carousel";
50
+
51
+ const carousel = createCarousel("[data-carousel]", {
52
+ loop: true,
53
+ initialIndex: 0
54
+ });
55
+
56
+ // programmatic control
57
+ carousel.next();
58
+ carousel.goTo(2, { immediate: false });
59
+ ```
60
+
61
+ The base stylesheet only controls slide visibility (`.is-active`) and disabled controls; layout remains entirely up to you.
62
+ Optional helper: add `.gsap-carousel__stack` to a slides wrapper to stack slides with `position: absolute`.
63
+ Slides can use `data-carousel-slide` or the optional `.gsap-carousel__slide` class; both work with `.gsap-carousel__stack`.
64
+
65
+ ## Transition lock behavior
66
+ While animating, all navigation requests are ignored and controls are disabled (buttons get `disabled`, everything gets `aria-disabled`).
67
+
68
+ ## Keyboard input
69
+ Arrow keys are active only when focus is within the carousel root. The root is given `tabindex=\"0\"` if none exists.
70
+
71
+ ## Built-in animations
72
+ Registered by default:
73
+ - `fade-in`
74
+ - `fade-up`
75
+ - `slide-in` (direction-aware)
76
+
77
+ If multiple classes match registered animation names, the first match in `classList` order wins.
78
+
79
+ ## Custom animation registration
80
+
81
+ ```ts
82
+ import { registerAnimation } from "@brandonsoccer22/gsap-carousel";
83
+ import type { AnimationFactory } from "@brandonsoccer22/gsap-carousel";
84
+
85
+ const popIn: AnimationFactory = ({ el, tl, opts }) => {
86
+ tl.fromTo(
87
+ el,
88
+ { scale: 0.9, autoAlpha: 0 },
89
+ { scale: 1, autoAlpha: 1, duration: opts.dur, delay: opts.delay, ease: opts.ease },
90
+ opts.at
91
+ );
92
+ };
93
+
94
+ // Optional reverse factory (used for exit when data-exit is missing)
95
+ popIn.reverse = ({ el, tl, opts }) => {
96
+ tl.to(el, { scale: 0.98, autoAlpha: 0, duration: opts.dur, delay: opts.delay }, opts.at);
97
+ };
98
+
99
+ registerAnimation("pop-in", popIn);
100
+ ```
101
+
102
+ Example with SplitText + overlap
103
+
104
+ ```ts
105
+ import type { AnimationFactory } from "@brandonsoccer22/gsap-carousel";
106
+ import { createCarousel, registerAnimation, registerGsapPlugins } from "@brandonsoccer22/gsap-carousel";
107
+ import { gsap } from "gsap";
108
+ import SplitText from "gsap/SplitText";
109
+
110
+ registerGsapPlugins(SplitText);
111
+
112
+ const splitLines: AnimationFactory = ({ el, tl, gsap, opts }) => {
113
+ const split = new SplitText(el, { type: "lines" });
114
+
115
+ gsap.set(split.lines, { yPercent: -120, opacity: 0 });
116
+
117
+ tl.to(
118
+ split.lines,
119
+ {
120
+ yPercent: 0,
121
+ opacity: 1,
122
+ duration: opts.dur,
123
+ ease: opts.ease,
124
+ stagger: 0.06
125
+ },
126
+ opts.at
127
+ );
128
+
129
+ tl.add(() => split.revert(), ">");
130
+ };
131
+
132
+ registerAnimation("split-lines", splitLines);
133
+
134
+ const splitLinesExit: AnimationFactory = ({ el, tl, opts }) => {
135
+ const split = new SplitText(el, { type: "lines" });
136
+
137
+ tl.to(
138
+ split.lines,
139
+ {
140
+ yPercent: 40,
141
+ opacity: 0,
142
+ duration: Math.min(opts.dur, 0.55),
143
+ ease: opts.ease,
144
+ stagger: 0.05
145
+ },
146
+ opts.at
147
+ );
148
+
149
+ tl.add(() => split.revert(), ">");
150
+ };
151
+
152
+ registerAnimation("split-lines-exit", splitLinesExit);
153
+
154
+ createCarousel("[data-carousel]", {
155
+ gsap,
156
+ transition: { overlap: 0.05 }
157
+ });
158
+ ```
159
+
160
+ ## Data attributes reference
161
+ - `data-seq="1"` sequence group (integer, default 1)
162
+ - `data-exit-seq="1"` exit sequence group (integer, defaults to `data-seq`)
163
+ - `data-dur="0.6"` duration in seconds (default from options)
164
+ - `data-delay="0"` delay in seconds (default 0)
165
+ - `data-at="-=0.4"` position offset in seconds for this element within its sequence; supports `+=`/`-=` or a plain number
166
+ - `data-ease="power2.out"` GSAP ease string
167
+ - `data-exit="fade-in"` explicit exit animation name
168
+
169
+ ## Reduced motion
170
+ If the user prefers reduced motion, slide switching happens immediately and element-level enter/exit animations are skipped.
171
+
172
+ ## Destroy
173
+
174
+ ```ts
175
+ carousel.destroy();
176
+ ```
177
+
178
+ `destroy()` removes all listeners, kills active timelines, reverts GSAP context, and removes generated dots (the template is unhidden).
179
+
180
+ ## API
181
+
182
+ ```ts
183
+ createCarousel(root, options?): CarouselInstance
184
+ registerAnimation(name, factory): void
185
+ registerAnimations(map): void
186
+ ```
187
+
188
+ ```ts
189
+ interface CarouselInstance {
190
+ goTo(index: number, opts?: { immediate?: boolean }): void
191
+ next(opts?: { immediate?: boolean }): void
192
+ prev(opts?: { immediate?: boolean }): void
193
+ destroy(): void
194
+ getIndex(): number
195
+ getCount(): number
196
+ isAnimating(): boolean
197
+ }
198
+ ```
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@brandonsoccer22/gsap-editorial-carousel",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "git+https://github.com/brandonsoccer22/gsap-editorial-carousel.git"
6
+ },
7
+ "version": "0.1.2",
8
+ "description": "A small, class-driven GSAP carousel controller with CMS-friendly animations.",
9
+ "type": "module",
10
+ "main": "./dist/index.cjs",
11
+ "module": "./dist/index.mjs",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.mjs",
17
+ "require": "./dist/index.cjs"
18
+ },
19
+ "./styles.css": "./styles.css"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "styles.css",
24
+ "README.md",
25
+ "package.json"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
+ "clean": "rm -rf dist"
31
+ },
32
+ "peerDependencies": {
33
+ "gsap": "^3.12.0"
34
+ },
35
+ "devDependencies": {
36
+ "gsap": "^3.12.0",
37
+ "tsup": "^8.0.1",
38
+ "typescript": "^5.4.0"
39
+ },
40
+ "keywords": [
41
+ "gsap",
42
+ "carousel",
43
+ "animation",
44
+ "typescript"
45
+ ],
46
+ "license": "MIT"
47
+ }
package/styles.css ADDED
@@ -0,0 +1,39 @@
1
+ .is-disabled {
2
+ opacity: 0.5;
3
+ pointer-events: none;
4
+ }
5
+
6
+ /* Base visibility only; layout remains user-controlled. */
7
+ [data-carousel] [data-carousel-slide],
8
+ [data-carousel] .gsap-carousel__slide {
9
+ opacity: 0;
10
+ pointer-events: none;
11
+ }
12
+
13
+ [data-carousel] [data-carousel-slide].is-active,
14
+ [data-carousel] .gsap-carousel__slide.is-active {
15
+ opacity: 1;
16
+ pointer-events: auto;
17
+ }
18
+
19
+ /* Optional layout helper (.gsap-carousel__stack): opt in when you want slides stacked. */
20
+ .gsap-carousel__stack {
21
+ position: relative;
22
+ }
23
+
24
+ .gsap-carousel__stack [data-carousel-slide],
25
+ .gsap-carousel__stack .gsap-carousel__slide {
26
+ position: absolute;
27
+ inset: 0;
28
+ }
29
+
30
+ /* Keep the active slide in flow so the stack can size to its content. */
31
+ .gsap-carousel__stack [data-carousel-slide].is-active {
32
+ position: relative;
33
+ inset: auto;
34
+ }
35
+
36
+ /* Set the point for the entire slide */
37
+ /* .is-animating {
38
+ cursor: progress;
39
+ } */