@duboseweb/motus 1.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.
@@ -0,0 +1,150 @@
1
+ # Customization
2
+
3
+ ## Custom animations
4
+
5
+ The JavaScript never knows animation names — it only toggles `motus-animate`. A new animation is therefore just CSS:
6
+
7
+ ```css
8
+ @media (prefers-reduced-motion: no-preference) {
9
+ html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) {
10
+ [data-motus='rotate-in'] {
11
+ opacity: 0;
12
+ transform-origin: bottom left;
13
+ transform: rotate(-14deg) scale(0.96);
14
+ transition:
15
+ opacity 500ms ease,
16
+ transform 700ms cubic-bezier(0.34, 1.56, 0.64, 1);
17
+ }
18
+
19
+ [data-motus='rotate-in'].motus-animate {
20
+ opacity: 1;
21
+ transform: none;
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ```html
28
+ <div data-motus="rotate-in"></div>
29
+ ```
30
+
31
+ Both wrappers matter, and for the same reason: every one of them is an escape route out of the
32
+ hidden `opacity: 0` state, for a visitor whose animation is never going to run.
33
+
34
+ - **`prefers-reduced-motion`** — someone who asked for reduced motion.
35
+ - **`.no-js`** — the script never executed.
36
+ - **`[data-motus-disabled]`** — the consumer's kill switch.
37
+ - **`[data-motus-inactive]`** — the library set this on `<html>` because it is not running:
38
+ disabled by `disable` (which by default is on below 992px), torn down by `destroy()`, or in a
39
+ browser without IntersectionObserver.
40
+
41
+ Miss one and the affected visitors are left looking at a blank space where your content should
42
+ be. The shipped families use exactly this selector; custom animations need it too.
43
+
44
+ Two things make that read as a rotation rather than a drift: `transform-origin` gives it a
45
+ pivot, and there is no `translate` competing with it. A few degrees of tilt alongside a vertical
46
+ translate just looks like a fade upwards.
47
+
48
+ You are not limited to opacity and transform. `demo/headless.html` drives a `clip-path` wipe and
49
+ a `filter` blur off the same class — neither is expressible with the shipped families.
50
+
51
+ > **Do not prefix a custom name with `fade`, `zoom`, `slide` or `flip`.** The shipped stylesheets match those families with `[data-motus^='fade']`-style selectors, so `fade-slow` would silently inherit `opacity: 0` from the fade family. Pick a name outside those prefixes.
52
+
53
+ ## Custom easing
54
+
55
+ Pass any `cubic-bezier()` directly:
56
+
57
+ ```html
58
+ <div data-motus="fade-up" data-motus-easing="cubic-bezier(.25, .25, .75, .75)"></div>
59
+ ```
60
+
61
+ Or set the default for every element:
62
+
63
+ ```js
64
+ Motus.init({ easing: 'cubic-bezier(.25, .25, .75, .75)' });
65
+ ```
66
+
67
+ ## Changing the travel distance
68
+
69
+ `$motus-distance` controls how far `fade-*` and `zoom-*` elements translate (default `100px`). Slides always move 100% of their own size and ignore it.
70
+
71
+ ```scss
72
+ @use '@duboseweb/motus/scss/config' with (
73
+ $motus-distance: 200px
74
+ );
75
+ @use '@duboseweb/motus/scss/core';
76
+ @use '@duboseweb/motus/scss/animations/fade';
77
+ @use '@duboseweb/motus/scss/animations/zoom';
78
+ ```
79
+
80
+ Configure `config` **before** any family is loaded — that is a Sass requirement for `!default` variables, not a quirk of this library.
81
+
82
+ ## Using Animate.css instead
83
+
84
+ Skip the motus stylesheets entirely and let the library drive Animate.css class names:
85
+
86
+ ```js
87
+ import Motus from '@duboseweb/motus';
88
+ import 'animate.css';
89
+
90
+ Motus.init({
91
+ useClassNames: true, // applies the data-motus value as class names
92
+ initClassName: false,
93
+ animatedClassName: 'animated',
94
+ });
95
+ ```
96
+
97
+ ```html
98
+ <div data-motus="fadeInUp"></div>
99
+ ```
100
+
101
+ You will usually also want:
102
+
103
+ ```css
104
+ [data-motus] {
105
+ visibility: hidden;
106
+ }
107
+ [data-motus].animated {
108
+ visibility: visible;
109
+ }
110
+ ```
111
+
112
+ ## Bring your own animations entirely
113
+
114
+ Import the JS and no CSS at all. The library adds `motus-init` on setup and `motus-animate` on entry, and fires `motus:in` / `motus:out` — everything visual is yours:
115
+
116
+ ```js
117
+ import Motus from '@duboseweb/motus'; // no stylesheet import
118
+ Motus.init({ initClassName: false });
119
+ ```
120
+
121
+ See `demo/headless.html` for a working example.
122
+
123
+ ## Triggering from another element
124
+
125
+ `data-motus-anchor` takes a CSS selector. The element animates based on the **anchor's** position, not its own — useful for revealing a whole section when its heading arrives:
126
+
127
+ ```html
128
+ <h2 id="section-2">Section two</h2>
129
+
130
+ <div data-motus="fade-up" data-motus-anchor="#section-2"></div>
131
+ <div data-motus="fade-up" data-motus-anchor="#section-2" data-motus-delay="100"></div>
132
+ <div data-motus="fade-up" data-motus-anchor="#section-2" data-motus-delay="200"></div>
133
+ ```
134
+
135
+ Elements sharing an anchor share a single observer, so this is cheap. An invalid selector logs a warning and falls back to observing the element itself.
136
+
137
+ ## Turning it off
138
+
139
+ At the HTML level, which disables both the CSS and the JS:
140
+
141
+ ```html
142
+ <html data-motus-disabled></html>
143
+ ```
144
+
145
+ Or at the JS level, including by device class or a predicate of your own:
146
+
147
+ ```js
148
+ Motus.init({ disable: 'phone' });
149
+ Motus.init({ disable: () => window.innerWidth < 640 });
150
+ ```
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DuBose Web
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,355 @@
1
+ # dwg-motus
2
+
3
+ Animate elements as they scroll into view, using the native **IntersectionObserver** API instead of scroll event listeners.
4
+
5
+ - **Zero runtime dependencies**
6
+ - **Modular** — take the whole stylesheet, one animation family, or none at all
7
+ - **Typed** — TypeScript source, bundled `.d.ts`
8
+ - **Accessible** — every animation is gated behind `prefers-reduced-motion`
9
+ - ~3.3 kB JS + ~0.6 kB CSS, gzipped
10
+
11
+ ```html
12
+ <div data-motus="fade-up">I animate when you scroll to me.</div>
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Why IntersectionObserver?
18
+
19
+ The original approach to scroll animation is a `scroll` listener that measures every element on every frame. IntersectionObserver hands that work to the browser, which does it off the main thread.
20
+
21
+ | | Scroll events | IntersectionObserver |
22
+ | ---------------------- | --------------------------- | ------------------------------------------ |
23
+ | Checks while scrolling | ~1000/sec with 100 elements | only when an element crosses the threshold |
24
+ | CPU when idle | continuous polling | zero |
25
+ | Battery impact | higher | much lower |
26
+ | Frame drops | likely with many elements | rare |
27
+ | Browser optimisation | none | native |
28
+
29
+ ---
30
+
31
+ ## Installation
32
+
33
+ ```sh
34
+ npm install @duboseweb/motus
35
+ ```
36
+
37
+ ```js
38
+ import Motus from '@duboseweb/motus';
39
+ import '@duboseweb/motus/motus.css';
40
+
41
+ Motus.init();
42
+ ```
43
+
44
+ The JS entry has **no stylesheet side effects** — CSS is always an explicit import. That is what makes the per-family imports below possible, and it keeps the package safe to import in SSR and CommonJS contexts.
45
+
46
+ ### Only the animations you use
47
+
48
+ `core.css` is required. Add only the families you actually reference:
49
+
50
+ ```js
51
+ import '@duboseweb/motus/css/core.css';
52
+ import '@duboseweb/motus/css/fade.css';
53
+ // zoom.css, slide.css and flip.css are never shipped to the browser
54
+ ```
55
+
56
+ ### From a CDN
57
+
58
+ ```html
59
+ <link rel="stylesheet" href="https://unpkg.com/@duboseweb/motus/dist/css/motus.css" />
60
+ <script src="https://unpkg.com/@duboseweb/motus/dist/motus.umd.js"></script>
61
+ <script>
62
+ Motus.init();
63
+ </script>
64
+ ```
65
+
66
+ ### Compiling the SCSS yourself
67
+
68
+ The Sass sources ship with the package, so you can override the translate distance and compile only what you need:
69
+
70
+ ```scss
71
+ @use '@duboseweb/motus/scss/config' with (
72
+ $motus-distance: 200px
73
+ );
74
+ @use '@duboseweb/motus/scss/core';
75
+ @use '@duboseweb/motus/scss/animations/fade';
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Usage
81
+
82
+ ### 1. Initialise
83
+
84
+ ```js
85
+ import Motus from '@duboseweb/motus';
86
+
87
+ Motus.init({
88
+ offset: 120, // px before the trigger point
89
+ delay: 0, // ms
90
+ duration: 400, // ms
91
+ easing: 'ease',
92
+ once: false, // animate only the first time
93
+ mirror: false, // animate back out when scrolling away
94
+ anchorPlacement: 'top-bottom',
95
+ disable: 'lg', // below the lg breakpoint — see Responsive breakpoints
96
+ breakpoints: { sm: 576, md: 768, lg: 992, xl: 1200, xxl: 1400 },
97
+ startEvent: 'DOMContentLoaded',
98
+ initClassName: 'motus-init',
99
+ animatedClassName: 'motus-animate',
100
+ useClassNames: false,
101
+ disableMutationObserver: false,
102
+ debounceDelay: 50, // resize debounce, clamped to 16–500
103
+ });
104
+ ```
105
+
106
+ Unrecognised keys and out-of-range values produce a single grouped `console.warn`, so a typo shows up immediately instead of silently doing nothing.
107
+
108
+ ### Responsive breakpoints
109
+
110
+ **By default motus does not animate below 992px.** Many animations only read well at desktop
111
+ width, so disabling outright is usually better than maintaining a parallel mobile set. Pass
112
+ `disable: false` to animate everywhere.
113
+
114
+ `disable` accepts a Bootstrap-aligned tier name, which means **below that tier** — it is
115
+ inclusive and downward, so `'lg'` covers everything narrower than `lg`:
116
+
117
+ | `disable` | Media query | Disabled on |
118
+ | --------- | ------------------------ | --------------------------------------------------------------- |
119
+ | `'sm'` | `(max-width: 575.98px)` | small phones |
120
+ | `'md'` | `(max-width: 767.98px)` | phones in portrait |
121
+ | `'lg'` | `(max-width: 991.98px)` | the above, plus phone landscape and tablet portrait _(default)_ |
122
+ | `'xl'` | `(max-width: 1199.98px)` | the above, plus tablet landscape |
123
+ | `'xxl'` | `(max-width: 1399.98px)` | the above, plus small laptops |
124
+
125
+ Override any tier individually — the rest keep their defaults:
126
+
127
+ ```js
128
+ Motus.init({ disable: 'lg', breakpoints: { lg: 1024 } });
129
+ ```
130
+
131
+ The `.98` is deliberate: a `max-width` derived from a `min-width` breakpoint must not leave a
132
+ dead zone on fractional viewport widths.
133
+
134
+ `disable` also accepts `true`, `false`, a `() => boolean` predicate, and the older device-class
135
+ keywords `'phone' | 'tablet' | 'mobile'`. Those three detect a touch pointer via `matchMedia`
136
+ rather than width, and are mutually exclusive — `'tablet'` does **not** also cover phones. Prefer
137
+ a tier name unless you specifically want touch detection.
138
+
139
+ The check runs once, at `init()`. A desktop window resized across the breakpoint keeps its
140
+ existing state until something calls [`refreshHard()`](#api).
141
+
142
+ ### 2. Mark up your elements
143
+
144
+ ```html
145
+ <div data-motus="fade-up"></div>
146
+ <div data-motus="zoom-in" data-motus-duration="800" data-motus-delay="200"></div>
147
+ <div data-motus="flip-left" data-motus-once="true"></div>
148
+ ```
149
+
150
+ Any global option can be overridden per element:
151
+
152
+ | Attribute | Notes |
153
+ | ----------------------------- | ------------------------------------------------------ |
154
+ | `data-motus` | the animation name (required) |
155
+ | `data-motus-offset` | px |
156
+ | `data-motus-delay` | ms |
157
+ | `data-motus-duration` | ms |
158
+ | `data-motus-easing` | a named easing or a raw `cubic-bezier(...)` |
159
+ | `data-motus-once` | `true` / `false` |
160
+ | `data-motus-mirror` | `true` / `false` |
161
+ | `data-motus-anchor` | CSS selector — trigger on _another_ element's position |
162
+ | `data-motus-anchor-placement` | see below |
163
+ | `data-motus-id` | scopes the `motus:in:<id>` event |
164
+
165
+ Add `data-motus-disabled` to `<html>` to switch everything off in both CSS and JS — useful as a server-rendered kill switch.
166
+
167
+ The library sets a second attribute, `data-motus-inactive`, on `<html>` whenever it is not
168
+ running — disabled by `disable`, torn down by `destroy()`, or in a browser without
169
+ IntersectionObserver. The stylesheet hides `[data-motus]` elements until they animate, so this
170
+ is what reveals them when no JS will ever arrive to do it. Treat it as read-only: it is cleared
171
+ on the next `init()`, and setting it yourself will be overwritten. Use `data-motus-disabled` for
172
+ a kill switch.
173
+
174
+ ---
175
+
176
+ ## API
177
+
178
+ ```js
179
+ Motus.init(options); // start; returns the matched elements
180
+ Motus.refresh(); // rebuild the observers against the current DOM
181
+ Motus.refreshHard(); // refresh, re-checking whether the library should be disabled
182
+ Motus.destroy(); // full teardown; safe to init() again afterwards
183
+ ```
184
+
185
+ The exported object is frozen. `refresh()` and `refreshHard()` are called for you on resize and on DOM mutation respectively, so you rarely need them directly — reach for `destroy()` on SPA route changes.
186
+
187
+ ### Events
188
+
189
+ `motus:in` and `motus:out` are dispatched on `document`:
190
+
191
+ ```js
192
+ document.addEventListener('motus:in', ({ detail }) => {
193
+ console.log('animated in', detail.node);
194
+ });
195
+ ```
196
+
197
+ With `data-motus-id="hero"` you also get `motus:in:hero`, fired _in addition to_ the base event.
198
+
199
+ > `detail.node` is the live DOM element, not a copy. Treat it as read-only — mutating it from a listener affects the page.
200
+
201
+ ### TypeScript
202
+
203
+ ```ts
204
+ import Motus, {
205
+ type MotusOptions,
206
+ type AnchorPlacement,
207
+ type BreakpointName,
208
+ } from '@duboseweb/motus';
209
+
210
+ Motus.init({
211
+ anchorPlacement: 'center-center', // union-typed, autocompletes
212
+ disable: 'lg', // BreakpointName | 'phone' | 'tablet' | 'mobile' | boolean | () => boolean
213
+ breakpoints: { lg: 1024 }, // partial — the other tiers keep their defaults
214
+ });
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Animations
220
+
221
+ **Fade** — `fade`, `fade-in`, `fade-up`, `fade-down`, `fade-left`, `fade-right`, `fade-up-left`, `fade-up-right`, `fade-down-left`, `fade-down-right`
222
+
223
+ **Zoom** — `zoom-in`, `zoom-in-up`, `zoom-in-down`, `zoom-in-left`, `zoom-in-right`, `zoom-out`, `zoom-out-up`, `zoom-out-down`, `zoom-out-left`, `zoom-out-right`
224
+
225
+ **Slide** — `slide-up`, `slide-down`, `slide-left`, `slide-right`
226
+
227
+ **Flip** — `flip-up`, `flip-down`, `flip-left`, `flip-right`
228
+
229
+ ### Anchor placements
230
+
231
+ `top-bottom` · `top-center` · `top-top` · `center-bottom` · `center-center` · `center-top` · `bottom-bottom` · `bottom-center` · `bottom-top`
232
+
233
+ The first word is the part of the **element**, the second is the part of the **viewport** it must reach.
234
+
235
+ ### Easings
236
+
237
+ `linear`, `ease`, `ease-in`, `ease-out`, `ease-in-out` pass straight through to CSS. These resolve to tuned `cubic-bezier()` values:
238
+
239
+ `ease-in-back` · `ease-out-back` · `ease-in-out-back` · `ease-in-sine` · `ease-out-sine` · `ease-in-out-sine` · `ease-in-quad` · `ease-out-quad` · `ease-in-out-quad` · `ease-in-cubic` · `ease-out-cubic` · `ease-in-out-cubic` · `ease-in-quart` · `ease-out-quart` · `ease-in-out-quart`
240
+
241
+ Anything else is passed through untouched, so a raw `cubic-bezier(.25,.1,.25,1)` works.
242
+
243
+ ---
244
+
245
+ ## Accessibility
246
+
247
+ Every animation rule lives inside `@media (prefers-reduced-motion: no-preference)`. Users who ask for reduced motion get the content immediately, fully visible and static — there is no JavaScript branch to get wrong, and no risk of content being stuck at `opacity: 0`.
248
+
249
+ Animations are also wrapped in `html:not(.no-js)`, so if you set `class="no-js"` on `<html>` and remove it from a small inline script, content stays visible when JavaScript fails to load.
250
+
251
+ ---
252
+
253
+ ## Troubleshooting
254
+
255
+ **Nothing animates, but the content is all visible.** This is almost always
256
+ `prefers-reduced-motion`, and it is the library working as intended. Every animation rule is
257
+ gated on it, so if the viewer has asked for reduced motion they get the content immediately,
258
+ static. Check with:
259
+
260
+ ```js
261
+ matchMedia('(prefers-reduced-motion: reduce)').matches;
262
+ ```
263
+
264
+ On macOS the setting is **System Settings → Accessibility → Display → Reduce motion**, and it
265
+ applies to every browser. Firefox picks a change up immediately; Chrome may need a restart,
266
+ which is why the two can briefly disagree. Firefox also reports `reduce` unconditionally when
267
+ `privacy.resistFingerprinting` is enabled.
268
+
269
+ Note that most browser-automation tools _override_ this: Playwright, for example, defaults to
270
+ `reducedMotion: 'no-preference'`, so an automated check can show animations running on a machine
271
+ where a real browser would correctly suppress them.
272
+
273
+ **Nothing animates and nothing is initialised.** Check that the `no-js` class is actually being
274
+ removed from `<html>`. Every animation is gated behind `html:not(.no-js)`, so if the script that
275
+ removes it is blocked or never runs, the whole library appears dead. Remove it from an inline
276
+ script in `<head>`, not an external file.
277
+
278
+ `demo/diagnose.html` in the repository checks all of the above and names the cause.
279
+
280
+ ---
281
+
282
+ ## Browser support
283
+
284
+ Requires native [IntersectionObserver](https://caniuse.com/intersectionobserver): **Chrome 51+, Firefox 55+, Safari 12.1+, Edge 79+**. No IE11, and no polyfill is bundled. On an unsupported browser `init()` warns and returns, leaving all content visible.
285
+
286
+ ---
287
+
288
+ ## What ships
289
+
290
+ | | |
291
+ | --------------------------------- | ----------------------------------------- |
292
+ | `dist/motus.js`, `dist/motus.cjs` | **unminified**, with readable identifiers |
293
+ | `dist/motus.umd.js` | minified, for `<script>` tags |
294
+ | `dist/motus.d.ts`, `.d.cts` | types for `import` and `require` |
295
+ | `dist/css/*.css` | minified, one file per animation family |
296
+ | `scss/**` | the Sass sources, for your own build |
297
+
298
+ The bundler entries are deliberately not minified. Your bundler minifies them again on the way
299
+ into your application, so the bytes reaching a browser are identical either way — what you get
300
+ back is a readable stack trace when something goes wrong inside the library. The UMD build is the
301
+ opposite case: it is loaded directly by a `<script>` tag, so it stays minified.
302
+
303
+ Source maps are not published. They would reference `src/*.ts` files the package does not contain,
304
+ so a debugger would report "source not found" while the maps took up nearly half the download.
305
+ The unminified builds serve the same purpose without the indirection.
306
+
307
+ ---
308
+
309
+ ## Development
310
+
311
+ ```sh
312
+ npm install
313
+ npm run dev # demo pages at http://localhost:8080 with live reload
314
+ npm test # vitest
315
+ npm run build # dist/
316
+ npm run lint
317
+ npm run test:pack # pack, install into a throwaway project, assert it works
318
+ npm run check:pkg # publint + are-the-types-wrong
319
+ ```
320
+
321
+ ### Releasing
322
+
323
+ Publishing runs from CI on a version tag, using npm
324
+ [trusted publishing](https://docs.npmjs.com/trusted-publishers) — there is no npm token stored
325
+ anywhere, and npm generates a provenance attestation automatically, linking the published tarball
326
+ to the exact commit and workflow that built it.
327
+
328
+ Work lands on `develop`. `main` is protected and takes pull requests only, so the version bump is
329
+ made on `develop` and the tag is cut from `main` after the merge:
330
+
331
+ ```sh
332
+ # on develop — bump the version without tagging yet
333
+ npm version patch --no-git-tag-version # or minor / major
334
+ git commit -am "Release v1.0.1"
335
+ git push
336
+
337
+ # open a pull request from develop to main and merge it
338
+
339
+ git checkout main && git pull
340
+ git tag v1.0.1
341
+ git push origin v1.0.1 # this is what triggers the release
342
+ ```
343
+
344
+ Tags are not covered by the branch protection, so the final push is what starts the publish.
345
+
346
+ The workflow refuses to publish if the tagged commit is not reachable from `main`, or if the tag
347
+ and `package.json` disagree. It fails loudly rather than skipping, so a release that did not happen
348
+ is never mistaken for one that did. `prepublishOnly` then runs lint, typecheck, tests, the build,
349
+ `check:pkg` and the package smoke test before anything reaches the registry.
350
+
351
+ ---
352
+
353
+ ## License
354
+
355
+ MIT © DuBose Web
@@ -0,0 +1 @@
1
+ @media (prefers-reduced-motion:no-preference){body.motus-ready [data-motus]{transition-delay:0s;transition-duration:var(--motus-duration);transition-timing-function:var(--motus-easing)}body.motus-ready [data-motus].motus-animate{transition-delay:var(--motus-delay)}}
@@ -0,0 +1 @@
1
+ @media screen and (prefers-reduced-motion:no-preference){html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=fade][data-motus^=fade]{opacity:0;transition-property:opacity,transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=fade][data-motus^=fade].motus-animate{opacity:1;transform:none}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-in],html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade]{transform:none}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up]{transform:translate3d(0,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down]{transform:translate3d(0,-100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-right]{transform:translate3d(-100px,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-left]{transform:translate3d(100px,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up-right]{transform:translate3d(-100px,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up-left]{transform:translate3d(100px,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down-right]{transform:translate3d(-100px,-100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down-left]{transform:translate3d(100px,-100px,0)}}
@@ -0,0 +1 @@
1
+ @media screen and (prefers-reduced-motion:no-preference){html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=flip][data-motus^=flip]{-webkit-backface-visibility:hidden;backface-visibility:hidden;transition-property:transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-left]{transform:perspective(2500px) rotateY(-100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-left].motus-animate{transform:perspective(2500px) rotateY(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-right]{transform:perspective(2500px) rotateY(100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-right].motus-animate{transform:perspective(2500px) rotateY(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-up]{transform:perspective(2500px) rotateX(-100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-up].motus-animate{transform:perspective(2500px) rotateX(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-down]{transform:perspective(2500px) rotateX(100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-down].motus-animate{transform:perspective(2500px) rotateX(0)}}
@@ -0,0 +1 @@
1
+ @media (prefers-reduced-motion:no-preference){body.motus-ready [data-motus]{transition-delay:0s;transition-duration:var(--motus-duration);transition-timing-function:var(--motus-easing)}body.motus-ready [data-motus].motus-animate{transition-delay:var(--motus-delay)}}@media screen and (prefers-reduced-motion:no-preference){html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=fade][data-motus^=fade]{opacity:0;transition-property:opacity,transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=fade][data-motus^=fade].motus-animate{opacity:1;transform:none}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-in],html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade]{transform:none}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up]{transform:translate3d(0,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down]{transform:translate3d(0,-100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-right]{transform:translate3d(-100px,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-left]{transform:translate3d(100px,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up-right]{transform:translate3d(-100px,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-up-left]{transform:translate3d(100px,100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down-right]{transform:translate3d(-100px,-100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=fade-down-left]{transform:translate3d(100px,-100px,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=zoom][data-motus^=zoom]{opacity:0;transition-property:opacity,transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=zoom][data-motus^=zoom].motus-animate{opacity:1;transform:translateZ(0) scale(1)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in]{transform:scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-up]{transform:translate3d(0,100px,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-down]{transform:translate3d(0,-100px,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-right]{transform:translate3d(-100px,0,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-left]{transform:translate3d(100px,0,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out]{transform:scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-up]{transform:translate3d(0,100px,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-down]{transform:translate3d(0,-100px,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-right]{transform:translate3d(-100px,0,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-left]{transform:translate3d(100px,0,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=slide][data-motus^=slide]{transition-property:transform;visibility:hidden}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=slide][data-motus^=slide].motus-animate{transform:translateZ(0);visibility:visible}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-up]{transform:translate3d(0,100%,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-down]{transform:translate3d(0,-100%,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-right]{transform:translate3d(-100%,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-left]{transform:translate3d(100%,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=flip][data-motus^=flip]{-webkit-backface-visibility:hidden;backface-visibility:hidden;transition-property:transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-left]{transform:perspective(2500px) rotateY(-100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-left].motus-animate{transform:perspective(2500px) rotateY(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-right]{transform:perspective(2500px) rotateY(100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-right].motus-animate{transform:perspective(2500px) rotateY(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-up]{transform:perspective(2500px) rotateX(-100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-up].motus-animate{transform:perspective(2500px) rotateX(0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-down]{transform:perspective(2500px) rotateX(100deg)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=flip-down].motus-animate{transform:perspective(2500px) rotateX(0)}}
@@ -0,0 +1 @@
1
+ @media screen and (prefers-reduced-motion:no-preference){html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=slide][data-motus^=slide]{transition-property:transform;visibility:hidden}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=slide][data-motus^=slide].motus-animate{transform:translateZ(0);visibility:visible}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-up]{transform:translate3d(0,100%,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-down]{transform:translate3d(0,-100%,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-right]{transform:translate3d(-100%,0,0)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=slide-left]{transform:translate3d(100%,0,0)}}
@@ -0,0 +1 @@
1
+ @media screen and (prefers-reduced-motion:no-preference){html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=zoom][data-motus^=zoom]{opacity:0;transition-property:opacity,transform}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus^=zoom][data-motus^=zoom].motus-animate{opacity:1;transform:translateZ(0) scale(1)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in]{transform:scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-up]{transform:translate3d(0,100px,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-down]{transform:translate3d(0,-100px,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-right]{transform:translate3d(-100px,0,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-in-left]{transform:translate3d(100px,0,0) scale(.6)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out]{transform:scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-up]{transform:translate3d(0,100px,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-down]{transform:translate3d(0,-100px,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-right]{transform:translate3d(-100px,0,0) scale(1.2)}html:not(.no-js):not([data-motus-disabled]):not([data-motus-inactive]) [data-motus=zoom-out-left]{transform:translate3d(100px,0,0) scale(1.2)}}