@excom/content-carousel 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.
Files changed (40) hide show
  1. package/.rush/temp/chunked-rush-logs/content-carousel.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/content-carousel.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/content-carousel.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +3 -0
  14. package/config/rig.json +5 -0
  15. package/content-carousel-slide.ts +19 -0
  16. package/content-carousel.ts +218 -0
  17. package/index.css +5 -0
  18. package/index.ts +24 -0
  19. package/package.json +43 -0
  20. package/rush-logs/content-carousel.apply-exports.cache.log +1 -0
  21. package/rush-logs/content-carousel.apply-exports.log +1 -0
  22. package/rush-logs/content-carousel.build_docs.cache.log +1 -0
  23. package/rush-logs/content-carousel.build_docs.log +1 -0
  24. package/rush-logs/content-carousel.build_package-metas.cache.log +1 -0
  25. package/rush-logs/content-carousel.build_package-metas.log +1 -0
  26. package/src/content-carousel.css +239 -0
  27. package/support/custom-elements.json +398 -0
  28. package/support/demos/fade.html +7 -0
  29. package/support/demos/manual.html +9 -0
  30. package/support/demos/simple.html +7 -0
  31. package/support/dist-docs/content-carousel-slide.md +20 -0
  32. package/support/dist-docs/content-carousel.md +184 -0
  33. package/support/docs/INTERNAL.md +4 -0
  34. package/support/docs/README.md +66 -0
  35. package/support/package-meta.json +287 -0
  36. package/support/tests/content-carousel.test.ts +534 -0
  37. package/support/tests/fade.view.test.ts +23 -0
  38. package/support/tests/manual.view.test.ts +34 -0
  39. package/support/tests/simple.view.test.ts +24 -0
  40. package/tsconfig.json +5 -0
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 35.694532999999865
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 55.47787799999992
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 112.92609100000004
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "../../packages/content-carousel": "../../packages/content-carousel:n8+/7TfT7BWuKysR6wAgrprpaS+oWYsVdAAvlNgBNcM=:"
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
3
+ "rigPackageName": "@excom/heft-rig",
4
+ "rigProfile": "default"
5
+ }
@@ -0,0 +1,19 @@
1
+ import { Neutron } from "@excom/neutron";
2
+
3
+ /**
4
+ * A single slide within `<content-carousel>`.
5
+ *
6
+ * @summary One rotating slide of a content-carousel.
7
+ */
8
+ export const ContentCarouselSlide = Neutron({
9
+ tag: "content-carousel-slide",
10
+ props: {
11
+ /**
12
+ * @option
13
+ * @state
14
+ * Marks this as the currently shown slide. The parent
15
+ * `<content-carousel>` keeps exactly one slide active.
16
+ */
17
+ isActive: Boolean,
18
+ },
19
+ });
@@ -0,0 +1,218 @@
1
+ import type { ContentCarouselSlide } from "./content-carousel-slide";
2
+ import { ConstructorType, Neutron, TEvent } from "@excom/neutron";
3
+
4
+ type T_HTMLContentCarouselSlideElement =
5
+ typeof ContentCarouselSlide.CustomElement;
6
+
7
+ export type ContentCarouselSlideChangedDetail = {
8
+ activeSlide: HTMLElement | null;
9
+ previousSlide: HTMLElement | null;
10
+ };
11
+
12
+ export type ContentCarouselSlideChangedEvent = TEvent & {
13
+ type: "content-carousel-slide-changed";
14
+ detail: ContentCarouselSlideChangedDetail;
15
+ };
16
+
17
+ /** Position of a `<content-carousel>` among its own slides. */
18
+ export interface ContentCarouselProvision {
19
+ /** Index of the active slide; `-1` when there are no slides. */
20
+ index: number;
21
+ /** Number of own slides (a nested carousel's slides are not counted). */
22
+ count: number;
23
+ /** Direction of the most recent move; `null` until the first move. */
24
+ lastMove: "forward" | "back" | null;
25
+ }
26
+
27
+ /**
28
+ * Rotates a set of slides — swipe-free, command-driven, auto-play optional.
29
+ * Nav buttons are plain `<button command="--next" commandfor="…">`; fires a
30
+ * change event other elements can react to.
31
+ *
32
+ * `.provision` holds the position (`{ index, count, lastMove }`) so a
33
+ * progress readout can bind to it with `prop("provision")`.
34
+ *
35
+ * @summary Slide rotator — auto-play, manual nav, slide or fade transition.
36
+ *
37
+ * @descendant content-carousel-slide - Slides to rotate through. The first,
38
+ * or whichever has `is-active`, is shown initially.
39
+ *
40
+ * @command --back - Shows the previous slide (wraps to last). Stops
41
+ * auto-play.
42
+ * @command --next - Shows the next slide (wraps to first). Stops auto-play.
43
+ *
44
+ * @fires content-carousel-slide-changed - After any slide change, manual or
45
+ * auto.
46
+ * @type ContentCarouselSlideChangedEvent
47
+ *
48
+ * @example
49
+ * <content-carousel auto-play="5">
50
+ * <content-carousel-slide is-active>One</content-carousel-slide>
51
+ * <content-carousel-slide>Two</content-carousel-slide>
52
+ * </content-carousel>
53
+ */
54
+ export const ContentCarousel = Neutron({
55
+ tag: "content-carousel",
56
+ props: {
57
+ /**
58
+ * @option
59
+ * Seconds between automatic slide advances. Unset disables auto-play.
60
+ */
61
+ autoPlay: Number,
62
+ /**
63
+ * @option
64
+ * Set when the element is being dragged. Used by CSS only to disable `transition`.
65
+ */
66
+ isScrubbing: Boolean,
67
+ /**
68
+ * @option
69
+ * Transition used when the active slide changes.
70
+ * @values slide | fade
71
+ * @default slide
72
+ */
73
+ slideAnimation: String,
74
+ /**
75
+ * @state
76
+ * Direction of the most recent slide change — drives the CSS
77
+ * animation direction.
78
+ * @values forward | back
79
+ */
80
+ lastMove: String,
81
+ /**
82
+ * @option
83
+ * @state
84
+ * Set automatically after manual navigation, or when the element
85
+ * leaves the document (a DOM move keeps auto-play running), to pause
86
+ * auto-play. Set it directly to pause / resume auto-play
87
+ * programmatically.
88
+ */
89
+ autoPlayStopped: Boolean,
90
+ /**
91
+ * @provision
92
+ * Position: `{ index, count, lastMove }` — the active slide's index
93
+ * among this carousel's own slides, how many there are, and the
94
+ * direction of the last move. Set on connect and after every slide
95
+ * change. Not reflected as an attribute.
96
+ * @type ContentCarouselProvision
97
+ */
98
+ provision: Object as unknown as ConstructorType<ContentCarouselProvision>,
99
+ // private state
100
+ autoPlayIntervalId: Object as unknown as ConstructorType<
101
+ ReturnType<typeof setInterval>
102
+ >,
103
+ },
104
+ })
105
+ .defineMethods({
106
+ intervalCallback: () => ({ _move: [false, false] }),
107
+ /** Show the previous / next slide; a manual move stops auto-play. */
108
+ // `any`: `getActiveSlide` is declared in this same call (circular type)
109
+ _move: (element: any, isBack: boolean, isManual: boolean) => {
110
+ const { getActiveSlide, autoPlay } = element;
111
+ const activeSlide =
112
+ getActiveSlide() as T_HTMLContentCarouselSlideElement | null;
113
+ const nextSlide = findSlide(element, activeSlide, isBack);
114
+ nextSlide?.setAttribute?.("is-active", "");
115
+ activeSlide?.removeAttribute("is-active");
116
+ const lastMove = isBack ? "back" : "forward";
117
+ return {
118
+ lastMove,
119
+ ...(isManual && autoPlay ? { autoPlayStopped: true } : {}),
120
+ provision: readProvision(element, lastMove),
121
+ emit: [
122
+ "content-carousel-slide-changed",
123
+ { detail: { activeSlide: nextSlide, previousSlide: activeSlide } },
124
+ ] as any,
125
+ };
126
+ },
127
+ getActiveSlide: (element) => {
128
+ const slides = getOwnSlides(element);
129
+ return {
130
+ // Explicit type: inference hits a circular type issue here
131
+ returns: (slides.find((slide) => slide.hasAttribute("is-active")) ||
132
+ slides[0] ||
133
+ null) as unknown as T_HTMLContentCarouselSlideElement | null,
134
+ };
135
+ },
136
+ /** The current `{ index, count, lastMove }`, read from the DOM. */
137
+ _readProvision: (element) => ({
138
+ returns: readProvision(element, element.lastMove),
139
+ }),
140
+ })
141
+ .onConnected(({ _readProvision }) => ({
142
+ provision: _readProvision() as ContentCarouselProvision,
143
+ }))
144
+ .onPropChanged(
145
+ "autoPlay",
146
+ (
147
+ { autoPlay, autoPlayIntervalId, intervalCallback, autoPlayStopped },
148
+ previous
149
+ ) => {
150
+ if (previous.autoPlay && autoPlayIntervalId) {
151
+ clearInterval(autoPlayIntervalId);
152
+ }
153
+ return {
154
+ autoPlayIntervalId:
155
+ autoPlay && !autoPlayStopped
156
+ ? setInterval(intervalCallback, autoPlay * 1000)
157
+ : null,
158
+ };
159
+ }
160
+ )
161
+ .onDisconnected(
162
+ // A DOM move keeps the interval running; only a real removal stops auto-play
163
+ ({ autoPlay, isMoving }) =>
164
+ !isMoving &&
165
+ autoPlay && {
166
+ autoPlayStopped: true,
167
+ }
168
+ )
169
+ .onPropSet("autoPlayStopped", ({ autoPlayIntervalId }) => {
170
+ if (autoPlayIntervalId || autoPlayIntervalId === 0) {
171
+ clearInterval(autoPlayIntervalId);
172
+ return {
173
+ autoPlayIntervalId: null,
174
+ };
175
+ }
176
+ })
177
+ .onCommand("--back", () => ({ _move: [true, true] }))
178
+ .onCommand("--next", () => ({ _move: [false, true] }));
179
+
180
+ /** A fresh provision object from the carousel's own slides. */
181
+ function readProvision(
182
+ element: HTMLElement,
183
+ lastMove: string | null | undefined
184
+ ): ContentCarouselProvision {
185
+ const slides = getOwnSlides(element);
186
+ const active =
187
+ slides.find((slide) => slide.hasAttribute("is-active")) || slides[0];
188
+ return {
189
+ index: active ? slides.indexOf(active) : -1,
190
+ count: slides.length,
191
+ lastMove: lastMove === "forward" || lastMove === "back" ? lastMove : null,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Slides this carousel owns, in document order. A nested carousel keeps
197
+ * its own slides, excluded here the same way `<content-tabs>` scopes
198
+ * headers / bodies.
199
+ */
200
+ function getOwnSlides(element: HTMLElement): HTMLElement[] {
201
+ return [...element.querySelectorAll("content-carousel-slide")].filter(
202
+ (slide) => slide.closest("content-carousel") === element
203
+ ) as HTMLElement[];
204
+ }
205
+
206
+ function findSlide(
207
+ element: HTMLElement,
208
+ currentSlide: T_HTMLContentCarouselSlideElement | null,
209
+ isPrevious: boolean
210
+ ): T_HTMLContentCarouselSlideElement | null {
211
+ if (!currentSlide) return null;
212
+ const slides = getOwnSlides(element);
213
+ const currentIndex = slides.indexOf(currentSlide as unknown as HTMLElement);
214
+ if (currentIndex < 0) return null;
215
+ const nextIndex =
216
+ (currentIndex + (isPrevious ? -1 : 1) + slides.length) % slides.length;
217
+ return slides[nextIndex] as unknown as T_HTMLContentCarouselSlideElement;
218
+ }
package/index.css ADDED
@@ -0,0 +1,5 @@
1
+ @import "./src/content-carousel.css";
2
+
3
+ @layer {
4
+ @mixin module-content-carousel;
5
+ }
package/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { ContentCarousel } from "./content-carousel";
2
+ import { ContentCarouselSlide } from "./content-carousel-slide";
3
+
4
+ ContentCarousel.define();
5
+ ContentCarouselSlide.define();
6
+
7
+ export { ContentCarousel, ContentCarouselSlide };
8
+
9
+ type T_HTMLContentCarouselElement = typeof ContentCarousel.CustomElement;
10
+ type T_HTMLContentCarouselSlideElement =
11
+ typeof ContentCarouselSlide.CustomElement;
12
+ declare global {
13
+ interface HTMLContentCarouselElement extends T_HTMLContentCarouselElement {}
14
+ interface HTMLContentCarouselSlideElement extends T_HTMLContentCarouselSlideElement {}
15
+ interface Window {
16
+ HTMLContentCarouselElement: HTMLContentCarouselElement;
17
+ HTMLContentCarouselSlideElement: HTMLContentCarouselSlideElement;
18
+ }
19
+ interface HTMLElementTagNameMap {
20
+ "content-carousel": HTMLContentCarouselElement;
21
+ "content-carousel-slide": HTMLContentCarouselSlideElement;
22
+ }
23
+ }
24
+ export type { HTMLContentCarouselElement, HTMLContentCarouselSlideElement };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@excom/content-carousel",
3
+ "version": "0.1.0",
4
+ "description": "<content-carousel> custom element",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24.13.0"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@excom/kit-utils": "^0.1.0",
12
+ "@excom/neutron": "^0.1.0"
13
+ },
14
+ "peerDependencies": {},
15
+ "devDependencies": {
16
+ "@excom/heft-rig": "^0.1.0"
17
+ },
18
+ "repository": {
19
+ "url": "excom-dev/nucleus",
20
+ "directory": "packages/content-carousel"
21
+ },
22
+ "homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/content-carousel/support/docs/README.md",
23
+ "bugs": "https://github.com/excom-dev/nucleus/issues",
24
+ "keywords": [
25
+ "content-carousel",
26
+ "neutron",
27
+ "custom-elements"
28
+ ],
29
+ "excom": {
30
+ "packageType": "kit-element"
31
+ },
32
+ "scripts": {
33
+ "build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
34
+ "build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
35
+ "format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
36
+ "test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
37
+ "coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
38
+ "dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
39
+ "preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
40
+ "build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
41
+ "build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
42
+ }
43
+ }
@@ -0,0 +1 @@
1
+ Caching has been disabled for this project's "apply-exports" command.
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Styles for `<content-carousel>`.
3
+ * @element content-carousel
4
+ */
5
+
6
+ /* Aliases */
7
+ @custom-selector :--content-carousel content-carousel, .tag-content-carousel;
8
+ @custom-selector :--content-carousel--last-move [last-move], [data-last-move];
9
+ @custom-selector :--content-carousel--last-move-forward [last-move="forward"], [data-last-move="forward"];
10
+ @custom-selector :--content-carousel--last-move-back [last-move="back"], [data-last-move="back"];
11
+ @custom-selector :--content-carousel--slide-animation [slide-animation], [data-slide-animation];
12
+ @custom-selector :--content-carousel--slide-animation-slide [slide-animation="slide"], [data-slide-animation="slide"];
13
+ @custom-selector :--content-carousel--slide-animation-fade [slide-animation="fade"], [data-slide-animation="fade"];
14
+ @custom-selector :--content-carousel--slide-animation-track [slide-animation="track"], [data-slide-animation="track"];
15
+ @custom-selector :--content-carousel--is-scrubbing [is-scrubbing], [data-scrubbing];
16
+ @custom-selector :--content-carousel-slide content-carousel-slide, .tag-content-carousel-slide;
17
+ @custom-selector :--content-carousel-slide--is-active [is-active], [aria-current];
18
+
19
+ @define-mixin module-content-carousel {
20
+ :root {
21
+ /**
22
+ * Length of the slide / fade transition.
23
+ * @cssproperty
24
+ * @syntax <time>
25
+ * @default 0.25s
26
+ */
27
+ --content-carousel-slide-animation-duration: var(
28
+ --v-transition-duration-standard,
29
+ 0.25s
30
+ );
31
+ /**
32
+ * Easing used for the fade transition.
33
+ * @cssproperty
34
+ * @syntax <easing-function>
35
+ * @default ease-out
36
+ */
37
+ --content-carousel-transition-ease: var(--v-transition-ease-out, ease-out);
38
+ /**
39
+ * Min-width of the nav buttons.
40
+ * @cssproperty
41
+ * @syntax <length>
42
+ * @default 25px
43
+ */
44
+ --content-carousel-button-size: 25px;
45
+ }
46
+
47
+ :--content-carousel {
48
+ /**
49
+ * `slide-animation="track"` only: how far the track is dragged, in slide
50
+ * widths — `1` shows the next slide, `-1` the previous. While
51
+ * `is-scrubbing` it follows a wrapping `<gesture-handler>`'s inherited
52
+ * `--gesture-progress` unless set explicitly — swipeable carousels.
53
+ * @cssproperty
54
+ * @syntax <number>
55
+ * @default 0
56
+ */
57
+ --content-carousel-progress: 0;
58
+ display: block;
59
+ position: relative;
60
+ overflow: hidden;
61
+ width: 100%;
62
+ height: auto;
63
+ white-space: nowrap;
64
+
65
+ > [rel="prev"],
66
+ .tag-content-carousel-prev,
67
+ > [rel="next"],
68
+ .tag-content-carousel-next {
69
+ position: absolute;
70
+ top: calc(50% - calc(var(--content-carousel-button-size) / 2));
71
+ min-width: var(--content-carousel-button-size);
72
+ z-index: 3;
73
+ text-align: center;
74
+ cursor: pointer;
75
+ }
76
+
77
+ /**
78
+ * Positions a direct child with `rel="prev"` (or
79
+ * `.tag-content-carousel-prev`) as the "previous slide" nav control.
80
+ * @cssclass tag-content-carousel-prev
81
+ */
82
+ > [rel="prev"],
83
+ .tag-content-carousel-prev {
84
+ left: 0;
85
+ }
86
+ /**
87
+ * Positions a direct child with `rel="next"` (or
88
+ * `.tag-content-carousel-next`) as the "next slide" nav control.
89
+ * @cssclass tag-content-carousel-next
90
+ */
91
+ > [rel="next"],
92
+ .tag-content-carousel-next {
93
+ right: 0;
94
+ }
95
+
96
+ :--content-carousel-slide {
97
+ position: absolute;
98
+ top: 0;
99
+ left: 0;
100
+ width: 100%;
101
+ height: auto;
102
+ display: block;
103
+ }
104
+
105
+ /* slide / default */
106
+ &:not(:--content-carousel--slide-animation),
107
+ &:--content-carousel--slide-animation-slide {
108
+ :--content-carousel-slide {
109
+ animation-duration: var(--content-carousel-slide-animation-duration);
110
+ animation-fill-mode: forwards;
111
+ z-index: -1;
112
+ }
113
+ /* Initial state. No animation. */
114
+ &:not(:--content-carousel--last-move) :--content-carousel-slide {
115
+ &:--content-carousel-slide--is-active {
116
+ transform: translateX(0);
117
+ z-index: 2;
118
+ }
119
+ &:not(:--content-carousel-slide--is-active) {
120
+ opacity: 0;
121
+ z-index: 1;
122
+ }
123
+ }
124
+ &:--content-carousel--last-move-forward :--content-carousel-slide {
125
+ &:--content-carousel-slide--is-active {
126
+ animation-name: right-to-center;
127
+ z-index: 2;
128
+ }
129
+ &:not(:--content-carousel-slide--is-active):has(
130
+ + :--content-carousel-slide--is-active
131
+ ),
132
+ &:first-of-type:--content-carousel-slide--is-active ~ :last-of-type {
133
+ animation-name: center-to-left;
134
+ z-index: 1;
135
+ }
136
+ }
137
+ &:--content-carousel--last-move-back :--content-carousel-slide {
138
+ &:--content-carousel-slide--is-active {
139
+ animation-name: left-to-center;
140
+ z-index: 2;
141
+ }
142
+ &:--content-carousel-slide--is-active
143
+ + :not(:--content-carousel-slide--is-active),
144
+ &:first-of-type:not(:--content-carousel-slide--is-active):has(
145
+ ~ :last-of-type:--content-carousel-slide--is-active
146
+ ) {
147
+ animation-name: center-to-right;
148
+ z-index: 1;
149
+ }
150
+ }
151
+ }
152
+
153
+ /* fade */
154
+ &:--content-carousel--slide-animation-fade {
155
+ :--content-carousel-slide {
156
+ opacity: 0;
157
+ z-index: 1;
158
+ transition: opacity var(--content-carousel-transition-ease);
159
+ transition-duration: var(--content-carousel-slide-animation-duration);
160
+ &:--content-carousel-slide--is-active {
161
+ opacity: 1;
162
+ z-index: 2;
163
+ }
164
+ &:not(:--content-carousel-slide--is-active) {
165
+ opacity: 0;
166
+ z-index: 1;
167
+ }
168
+ }
169
+ }
170
+
171
+ /* track: the active slide sits at 0, neighbours at ±100%, all shifted by
172
+ `--content-carousel-progress`. Transitions animate a slide change;
173
+ none while `is-scrubbing`, when a finger drives the offset. */
174
+ &:--content-carousel--slide-animation-track {
175
+ :--content-carousel-slide {
176
+ --content-carousel-slot: 1;
177
+ transform: translateX(
178
+ calc(
179
+ (var(--content-carousel-slot) - var(--content-carousel-progress)) *
180
+ 100%
181
+ )
182
+ );
183
+ transition: transform var(--content-carousel-slide-animation-duration)
184
+ var(--content-carousel-transition-ease);
185
+ &:--content-carousel-slide--is-active {
186
+ --content-carousel-slot: 0;
187
+ z-index: 2;
188
+ }
189
+ &:has(~ :--content-carousel-slide--is-active) {
190
+ --content-carousel-slot: -1;
191
+ }
192
+ &:is(&:--content-carousel-slide--is-active + & + &),
193
+ &:has(+ & + &:--content-carousel-slide--is-active) {
194
+ /* hide slides that are two slides away */
195
+ display: none !important;
196
+ }
197
+ }
198
+ &:--content-carousel--is-scrubbing {
199
+ --content-carousel-progress: var(--gesture-progress, 0);
200
+ :--content-carousel-slide {
201
+ transition: none;
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ @keyframes right-to-center {
208
+ 0% {
209
+ transform: translateX(100%);
210
+ }
211
+ 100% {
212
+ transform: translateX(0);
213
+ }
214
+ }
215
+ @keyframes center-to-left {
216
+ 0% {
217
+ transform: translateX(0);
218
+ }
219
+ 100% {
220
+ transform: translateX(-100%);
221
+ }
222
+ }
223
+ @keyframes left-to-center {
224
+ 0% {
225
+ transform: translateX(-100%);
226
+ }
227
+ 100% {
228
+ transform: translateX(0);
229
+ }
230
+ }
231
+ @keyframes center-to-right {
232
+ 0% {
233
+ transform: translateX(0);
234
+ }
235
+ 100% {
236
+ transform: translateX(100%);
237
+ }
238
+ }
239
+ }