css-motion-library 0.0.0-stage → 1.0.1
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 +21 -0
- package/README.md +121 -2
- package/catalog.json +77 -0
- package/examples/001-commerce-storyboard/README.md +19 -0
- package/examples/001-commerce-storyboard/animation.css +167 -0
- package/examples/001-commerce-storyboard/index.html +146 -0
- package/examples/002-commerce-film/README.md +38 -0
- package/examples/002-commerce-film/animation.css +197 -0
- package/examples/002-commerce-film/index.html +73 -0
- package/examples/003-kinetic-headline/README.md +29 -0
- package/examples/003-kinetic-headline/animation.css +276 -0
- package/examples/003-kinetic-headline/index.html +82 -0
- package/examples/004-loading-states/README.md +28 -0
- package/examples/004-loading-states/animation.css +87 -0
- package/examples/004-loading-states/index.html +119 -0
- package/examples/005-orbit-diagram/README.md +30 -0
- package/examples/005-orbit-diagram/animation.css +123 -0
- package/examples/005-orbit-diagram/index.html +66 -0
- package/package.json +31 -4
- package/src/auto.js +2 -0
- package/src/demo.css +12 -0
- package/src/player.js +140 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrew Rico and CSS Motion Library contributors
|
|
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
CHANGED
|
@@ -1,3 +1,122 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CSS Motion Library
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/Andrewrico/css-motion-library/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/css-motion-library)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Copy-paste CSS animations with a tiny, accessible playback controller.**
|
|
8
|
+
No framework, no build step, no runtime dependencies.
|
|
9
|
+
|
|
10
|
+
**[Live gallery →](https://andrewrico.github.io/css-motion-library/)**
|
|
11
|
+
|
|
12
|
+
- **Pure CSS motion.** Every visual is HTML, CSS keyframes and inline SVG. No video files,
|
|
13
|
+
no web fonts, no animation engine.
|
|
14
|
+
- **Video-style controls when you want them.** A 6 KB, dependency-free ES module (`src/player.js`) adds
|
|
15
|
+
pause, replay, seeking and playback speed to any CSS animation timeline.
|
|
16
|
+
- **Accessible by default.** Each entry has a readable static version for reduced motion
|
|
17
|
+
and no-JavaScript, pauses when offscreen or in a background tab, and has no flashing.
|
|
18
|
+
- **Copy one thing.** Each entry is self-contained: one HTML snippet, one CSS file.
|
|
19
|
+
|
|
20
|
+
## The collection
|
|
21
|
+
|
|
22
|
+
| # | Entry | What it is | Loop |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| 001 | [Commerce Storyboard](examples/001-commerce-storyboard/) | Four illustrated process steps with restrained transitions | 24s |
|
|
25
|
+
| 002 | [Commerce Film](examples/002-commerce-film/) | Six-shot cinematic sequence: kinetic type, wipes, camera moves | 30s |
|
|
26
|
+
| 003 | [Kinetic Headline](examples/003-kinetic-headline/) | Masked word-by-word hero headline with a hand-drawn underline | 8s |
|
|
27
|
+
| 004 | [Loading States](examples/004-loading-states/) | Skeleton shimmer, progress, bouncing dots, spinner → check | 6s |
|
|
28
|
+
| 005 | [Orbit Diagram](examples/005-orbit-diagram/) | Explainer diagram: satellites orbit a core and highlight in turn | 12s |
|
|
29
|
+
|
|
30
|
+
Each entry folder has a README with its timeline, theme variables and customization notes.
|
|
31
|
+
[`catalog.json`](catalog.json) is the machine-readable index.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
Copy files straight from this repo, or install from npm:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm install css-motion-library
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import {createMotionPlayer} from 'css-motion-library/player.js';
|
|
43
|
+
import 'css-motion-library/examples/003-kinetic-headline/animation.css'; // with a CSS-aware bundler
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Use an animation
|
|
47
|
+
|
|
48
|
+
1. Copy the entry's component markup (the `<section data-motion-player>` block) from its
|
|
49
|
+
`index.html`, plus its `animation.css`.
|
|
50
|
+
2. Copy [`src/player.js`](src/player.js) and start it:
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<link rel="stylesheet" href="animation.css">
|
|
54
|
+
<!-- …the entry's <section data-motion-player> markup… -->
|
|
55
|
+
<script type="module">
|
|
56
|
+
import {createMotionPlayer} from './player.js';
|
|
57
|
+
document.querySelectorAll('[data-motion-player]').forEach(createMotionPlayer);
|
|
58
|
+
</script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. Change the copy in the HTML and the colors through the custom properties listed in the
|
|
62
|
+
entry README (for example `--ink`, `--accent`).
|
|
63
|
+
|
|
64
|
+
Entry 001 uses its own small inline script instead of the shared player; see its README.
|
|
65
|
+
|
|
66
|
+
### Player API
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const player = createMotionPlayer(element); // returns null if the browser lacks support
|
|
70
|
+
player.play();
|
|
71
|
+
player.pause();
|
|
72
|
+
player.seek(4.5); // seconds
|
|
73
|
+
player.restart();
|
|
74
|
+
player.currentTime; // seconds
|
|
75
|
+
player.playing; // boolean
|
|
76
|
+
player.destroy(); // removes listeners and restores the static state
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The player reads `data-duration` (seconds) from the root and controls every CSS animation
|
|
80
|
+
inside `[data-motion-stage]` as one timeline. It hides its controls and keeps the static
|
|
81
|
+
overview when the user prefers reduced motion or the host sets
|
|
82
|
+
`data-a11y-reduce-motion="true"` on `<html>`. An embedding page can pause an iframe'd entry
|
|
83
|
+
with `postMessage({type: 'css-motion:visibility', visible: false}, origin)` (same origin only).
|
|
84
|
+
|
|
85
|
+
## Browser support
|
|
86
|
+
|
|
87
|
+
Current Chrome, Edge, Firefox and Safari. The layouts use container query units (`cqw`) and
|
|
88
|
+
the player uses the Web Animations API (`element.getAnimations()`). Older browsers get the
|
|
89
|
+
static overview.
|
|
90
|
+
|
|
91
|
+
## Run locally
|
|
92
|
+
|
|
93
|
+
Requires Node.js 22+. No install needed to view the gallery:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
npm run dev # http://127.0.0.1:8092 (PORT=8093 npm run dev to change)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use HTTP rather than `file://`, because the player is an ES module.
|
|
100
|
+
|
|
101
|
+
## Checks
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npm test # player state, seeking, preferences, lifecycle (Node test runner)
|
|
105
|
+
npm run check # catalog, links, local assets, JavaScript syntax
|
|
106
|
+
npm install && npx playwright install chromium
|
|
107
|
+
npm run test:browser # real Chromium: every entry at 1440×900 and 390×844, seeking,
|
|
108
|
+
# reduced motion, no-JS, no console errors, no horizontal scroll
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
CI runs all three on every push and pull request. The gallery deploys to GitHub Pages
|
|
112
|
+
from `main` after CI passes.
|
|
113
|
+
|
|
114
|
+
## Contribute
|
|
115
|
+
|
|
116
|
+
New animations are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the entry checklist and
|
|
117
|
+
[DEPENDENCIES.md](DEPENDENCIES.md) before adding any third-party effect. Releases are listed in
|
|
118
|
+
[CHANGELOG.md](CHANGELOG.md).
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
[MIT](LICENSE) © 2026 Andrew Rico and CSS Motion Library contributors.
|
package/catalog.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "001-commerce-storyboard",
|
|
4
|
+
"title": "Commerce Storyboard",
|
|
5
|
+
"description": "Four illustrated steps, gentle transitions and restrained motion.",
|
|
6
|
+
"duration": 24,
|
|
7
|
+
"edition": "Original",
|
|
8
|
+
"entry": "examples/001-commerce-storyboard/index.html",
|
|
9
|
+
"styles": "examples/001-commerce-storyboard/animation.css",
|
|
10
|
+
"tags": [
|
|
11
|
+
"process",
|
|
12
|
+
"typography",
|
|
13
|
+
"cards",
|
|
14
|
+
"CSS"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "002-commerce-film",
|
|
19
|
+
"title": "Commerce Film",
|
|
20
|
+
"description": "Six cinematic shots, kinetic type, camera moves and a seekable player.",
|
|
21
|
+
"duration": 30,
|
|
22
|
+
"edition": "New",
|
|
23
|
+
"entry": "examples/002-commerce-film/index.html",
|
|
24
|
+
"styles": "examples/002-commerce-film/animation.css",
|
|
25
|
+
"tags": [
|
|
26
|
+
"cinematic",
|
|
27
|
+
"typography",
|
|
28
|
+
"CSS",
|
|
29
|
+
"timeline"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "003-kinetic-headline",
|
|
34
|
+
"title": "Kinetic Headline",
|
|
35
|
+
"description": "Masked word-by-word headline reveal with a hand-drawn underline and CTA.",
|
|
36
|
+
"duration": 8,
|
|
37
|
+
"edition": "New",
|
|
38
|
+
"entry": "examples/003-kinetic-headline/index.html",
|
|
39
|
+
"styles": "examples/003-kinetic-headline/animation.css",
|
|
40
|
+
"tags": [
|
|
41
|
+
"hero",
|
|
42
|
+
"typography",
|
|
43
|
+
"SVG",
|
|
44
|
+
"CSS"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "004-loading-states",
|
|
49
|
+
"title": "Loading States",
|
|
50
|
+
"description": "Skeleton shimmer, progress bar, bouncing dots and a spinner-to-check success state.",
|
|
51
|
+
"duration": 6,
|
|
52
|
+
"edition": "New",
|
|
53
|
+
"entry": "examples/004-loading-states/index.html",
|
|
54
|
+
"styles": "examples/004-loading-states/animation.css",
|
|
55
|
+
"tags": [
|
|
56
|
+
"UI",
|
|
57
|
+
"loaders",
|
|
58
|
+
"micro-interaction",
|
|
59
|
+
"CSS"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "005-orbit-diagram",
|
|
64
|
+
"title": "Orbit Diagram",
|
|
65
|
+
"description": "Explainer diagram: satellites orbit a core and highlight one at a time with captions.",
|
|
66
|
+
"duration": 12,
|
|
67
|
+
"edition": "New",
|
|
68
|
+
"entry": "examples/005-orbit-diagram/index.html",
|
|
69
|
+
"styles": "examples/005-orbit-diagram/animation.css",
|
|
70
|
+
"tags": [
|
|
71
|
+
"diagram",
|
|
72
|
+
"explainer",
|
|
73
|
+
"SVG",
|
|
74
|
+
"CSS"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 001 — Commerce Storyboard
|
|
2
|
+
|
|
3
|
+
The original four-step CSS introduction. Preserved as the first library entry.
|
|
4
|
+
|
|
5
|
+
- Story: Audit → Plan → Build → Measure.
|
|
6
|
+
- Timing: four six-second scenes, 24 seconds per loop.
|
|
7
|
+
- Style: dark canvas, yellow highlights, illustrated cards and subtle movement.
|
|
8
|
+
- Controls: pause/resume; automatic offscreen and background-tab pause.
|
|
9
|
+
- Fallback: static overview without JavaScript or with reduced motion enabled.
|
|
10
|
+
- Source: `source.njk` and `animation.css` preserve the first website component from
|
|
11
|
+
andrewrico.com, commit `dd35b1d9`. The standalone HTML uses generic “Your studio” branding
|
|
12
|
+
and system fonts so the demo has no dependency on that website or its assets.
|
|
13
|
+
|
|
14
|
+
Use `index.html` for a standalone version or `source.njk` in a Nunjucks project. Change the
|
|
15
|
+
copy and labels together. Its CSS expects `--color-primary` and font variables; the demo
|
|
16
|
+
provides fallback values through `../../src/demo.css`.
|
|
17
|
+
|
|
18
|
+
Keep this edition intact. Add new numbered entries when developing a different direction.
|
|
19
|
+
All original component code is covered by the repository MIT license.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* CSS storyboard: four six-second scenes. JS only handles playback preferences. */
|
|
2
|
+
.commerce-intro {
|
|
3
|
+
--intro-accent: var(--color-primary, #fffc00);
|
|
4
|
+
--intro-duration: 24s;
|
|
5
|
+
color: #f5f5ef;
|
|
6
|
+
background: #070909;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
border-block: 1px solid #282c28;
|
|
9
|
+
}
|
|
10
|
+
.commerce-intro__inner {
|
|
11
|
+
max-width: 1440px;
|
|
12
|
+
margin-inline: auto;
|
|
13
|
+
padding: 32px clamp(22px, 6vw, 88px);
|
|
14
|
+
}
|
|
15
|
+
.commerce-intro__masthead,
|
|
16
|
+
.commerce-intro__footer {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
gap: 24px;
|
|
21
|
+
}
|
|
22
|
+
.commerce-intro__masthead { font-size: 12px; letter-spacing: .04em; }
|
|
23
|
+
.commerce-intro__masthead p { margin: 0; color: #f5f5ef; }
|
|
24
|
+
.commerce-intro__masthead p span { color: #a7afa8; margin-left: 12px; }
|
|
25
|
+
.commerce-intro__edition { color: #a7afa8; white-space: nowrap; }
|
|
26
|
+
.commerce-intro__stage { position: relative; display: grid; min-height: 350px; }
|
|
27
|
+
.commerce-intro.is-enhanced .commerce-intro__stage { min-height: 470px; }
|
|
28
|
+
.commerce-intro__overview { align-self: center; padding-block: 50px; }
|
|
29
|
+
.commerce-intro__eyebrow { color: var(--intro-accent); text-transform: uppercase; letter-spacing: .15em; font-size: 12px; margin: 0 0 22px; }
|
|
30
|
+
.commerce-intro__headline,
|
|
31
|
+
.commerce-intro__overview h2 {
|
|
32
|
+
font-family: var(--font-family-2, sans-serif);
|
|
33
|
+
font-size: clamp(44px, 6.3vw, 88px);
|
|
34
|
+
font-weight: 900;
|
|
35
|
+
line-height: 1.02;
|
|
36
|
+
letter-spacing: -.055em;
|
|
37
|
+
color: #f5f5ef;
|
|
38
|
+
margin: 0 0 25px;
|
|
39
|
+
}
|
|
40
|
+
.commerce-intro__overview h2 span,
|
|
41
|
+
.commerce-intro__headline span { color: var(--intro-accent); }
|
|
42
|
+
.commerce-intro__description { color: #aeb7af; font-size: clamp(15px, 1.35vw, 18px); line-height: 1.65; max-width: 380px; margin: 0; }
|
|
43
|
+
.commerce-intro__scene { display: none; }
|
|
44
|
+
.commerce-intro.is-enhanced .commerce-intro__overview { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); }
|
|
45
|
+
.commerce-intro.is-enhanced .commerce-intro__scene {
|
|
46
|
+
display: grid;
|
|
47
|
+
grid-area: 1 / 1;
|
|
48
|
+
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
|
|
49
|
+
gap: clamp(24px, 4vw, 60px);
|
|
50
|
+
align-items: center;
|
|
51
|
+
padding-block: 50px;
|
|
52
|
+
opacity: 0;
|
|
53
|
+
visibility: hidden;
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
animation: commerce-scene var(--intro-duration) linear infinite;
|
|
56
|
+
animation-delay: calc(var(--step) * 6s);
|
|
57
|
+
}
|
|
58
|
+
/* The first scene stays visible while waiting to enter the viewport. */
|
|
59
|
+
.commerce-intro.is-enhanced .commerce-intro__scene--1 { opacity: 1; visibility: visible; }
|
|
60
|
+
.commerce-intro__visual { position: relative; min-height: 330px; display: grid; place-items: center; }
|
|
61
|
+
.commerce-intro__orbit { position: absolute; width: 310px; height: 310px; border: 1px solid #383e25; border-radius: 50%; transform: rotate(-25deg) scaleX(1.2); }
|
|
62
|
+
.commerce-intro__orbit::after { content: ''; position: absolute; inset: 30px -25px; border: 1px solid #222820; border-radius: 50%; }
|
|
63
|
+
.commerce-intro__diagram {
|
|
64
|
+
position: relative;
|
|
65
|
+
width: 100%;
|
|
66
|
+
max-width: 350px;
|
|
67
|
+
padding: 24px;
|
|
68
|
+
border: 1px solid #555d3a;
|
|
69
|
+
border-radius: 14px;
|
|
70
|
+
background: #111610;
|
|
71
|
+
box-shadow: 0 25px 60px #0006;
|
|
72
|
+
transform: rotate(-3deg);
|
|
73
|
+
animation: commerce-float 6s ease-in-out infinite;
|
|
74
|
+
}
|
|
75
|
+
.commerce-intro__diagram-top { display: flex; justify-content: space-between; gap: 10px; color: #e5ebd9; font-size: 12px; padding-bottom: 20px; border-bottom: 1px solid #30392b; }
|
|
76
|
+
.commerce-intro__diagram-top > span:last-child { color: var(--intro-accent); }
|
|
77
|
+
.commerce-intro__audit-row { display: flex; align-items: center; justify-content: space-between; gap: 15px; font-size: 13px; color: #c5ccbf; padding-block: 20px; border-bottom: 1px solid #273024; }
|
|
78
|
+
.commerce-intro__audit-row i { display: block; width: 65px; height: 5px; border-radius: 3px; background: var(--intro-accent); }
|
|
79
|
+
.commerce-intro__audit-row:nth-child(3) i { width: 40px; opacity: .6; }
|
|
80
|
+
.commerce-intro__audit-row:nth-child(4) i { width: 55px; opacity: .8; }
|
|
81
|
+
.commerce-intro__scan { position: absolute; inset: 65px 16px auto; height: 1px; background: var(--intro-accent); box-shadow: 0 0 20px #fffc0066; animation: commerce-scan 6s ease-in-out infinite; }
|
|
82
|
+
.commerce-intro__visual-note { position: absolute; bottom: -10px; color: #8c9789; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; }
|
|
83
|
+
.commerce-intro__map { display: flex; align-items: center; flex-direction: column; padding-top: 20px; }
|
|
84
|
+
.commerce-intro__map span { display: block; padding: 10px 12px; border: 1px solid #69733e; border-radius: 5px; font-size: 11px; color: #dce3cd; text-align: center; }
|
|
85
|
+
.commerce-intro__map > span:first-child { background: var(--intro-accent); color: #111610; border-color: var(--intro-accent); }
|
|
86
|
+
.commerce-intro__map > div { display: flex; justify-content: center; gap: 16px; }
|
|
87
|
+
.commerce-intro__map > i { width: 1px; height: 22px; background: #69733e; }
|
|
88
|
+
.commerce-intro__store { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding-block: 24px; }
|
|
89
|
+
.commerce-intro__product { min-height: 135px; border: 1px solid #4a5633; border-radius: 8px; background: radial-gradient(ellipse at center, #818e39 0, #818e39 28%, transparent 29%), #20291b; }
|
|
90
|
+
.commerce-intro__product-copy { display: flex; flex-direction: column; justify-content: center; gap: 12px; }
|
|
91
|
+
.commerce-intro__product-copy i { display: block; height: 5px; background: #788464; border-radius: 3px; }
|
|
92
|
+
.commerce-intro__product-copy i:nth-child(2) { width: 70%; }
|
|
93
|
+
.commerce-intro__product-copy i:nth-child(3) { width: 40%; }
|
|
94
|
+
.commerce-intro__product-copy span { margin-top: 10px; padding: 10px 5px; background: var(--intro-accent); color: #111610; text-align: center; font-size: 10px; border-radius: 4px; }
|
|
95
|
+
.commerce-intro__build-label,
|
|
96
|
+
.commerce-intro__chart-label { font-size: 10px; color: #a9b59a; letter-spacing: .03em; }
|
|
97
|
+
.commerce-intro__bars { display: flex; align-items: flex-end; gap: 12px; height: 170px; margin-block: 24px 16px; border-bottom: 1px solid #69733e; }
|
|
98
|
+
.commerce-intro__bars i { flex: 1; background: #697631; height: 35%; border-radius: 4px 4px 0 0; transform-origin: bottom; animation: commerce-bars 6s ease-in-out infinite; }
|
|
99
|
+
.commerce-intro__bars i:nth-child(2) { height: 52%; }
|
|
100
|
+
.commerce-intro__bars i:nth-child(3) { height: 46%; }
|
|
101
|
+
.commerce-intro__bars i:nth-child(4) { height: 68%; }
|
|
102
|
+
.commerce-intro__bars i:nth-child(5) { height: 60%; }
|
|
103
|
+
.commerce-intro__bars i:nth-child(6) { height: 83%; background: var(--intro-accent); }
|
|
104
|
+
.commerce-intro__footer { padding-top: 22px; border-top: 1px solid #30352c; }
|
|
105
|
+
.commerce-intro__steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); flex: 1; gap: 20px; list-style: none; padding: 0; margin: 0; }
|
|
106
|
+
.commerce-intro__steps li { position: relative; padding-top: 12px; font-size: 13px; color: #bbc4b6; border-top: 2px solid #343c2d; }
|
|
107
|
+
.commerce-intro__step-number { color: #818e76; font-size: 10px; margin-right: 6px; }
|
|
108
|
+
.commerce-intro.is-enhanced .commerce-intro__steps li::before { content: ''; position: absolute; inset: -2px 0 auto; height: 2px; background: var(--intro-accent); transform: scaleX(0); transform-origin: left; animation: commerce-progress var(--intro-duration) linear infinite; animation-delay: calc(var(--step) * 6s); }
|
|
109
|
+
.commerce-intro__toggle { display: inline-flex; gap: 10px; align-items: center; justify-content: center; flex-shrink: 0; min-height: 44px; padding: 8px 14px; border: 1px solid #566047; border-radius: 30px; color: #dce3cd; background: transparent; font: inherit; font-size: 11px; cursor: pointer; }
|
|
110
|
+
.commerce-intro__toggle[hidden] { display: none; }
|
|
111
|
+
.commerce-intro__toggle:hover { border-color: var(--intro-accent); color: var(--intro-accent); }
|
|
112
|
+
.commerce-intro__toggle:focus-visible { outline: 2px solid var(--intro-accent); outline-offset: 4px; }
|
|
113
|
+
.commerce-intro__sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
114
|
+
/* Pause all scenes, decorative loops and progress together. */
|
|
115
|
+
.commerce-intro:not(.is-playing) *,
|
|
116
|
+
.commerce-intro:not(.is-playing) *::before { animation-play-state: paused; }
|
|
117
|
+
@keyframes commerce-scene {
|
|
118
|
+
0%, 23% { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
119
|
+
25%, 98% { opacity: 0; visibility: hidden; transform: translateY(-12px); }
|
|
120
|
+
99.99% { opacity: 0; visibility: hidden; transform: translateY(12px); }
|
|
121
|
+
100% { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
122
|
+
}
|
|
123
|
+
@keyframes commerce-progress {
|
|
124
|
+
0% { transform: scaleX(0); }
|
|
125
|
+
24%, 25% { transform: scaleX(1); }
|
|
126
|
+
25.01%, 100% { transform: scaleX(0); }
|
|
127
|
+
}
|
|
128
|
+
@keyframes commerce-float { 0%, 100% { transform: translateY(0) rotate(-3deg); } 50% { transform: translateY(-8px) rotate(-1deg); } }
|
|
129
|
+
@keyframes commerce-scan { 0%, 100% { transform: translateY(0); opacity: .2; } 50% { transform: translateY(170px); opacity: .75; } }
|
|
130
|
+
@keyframes commerce-bars { 0%, 100% { transform: scaleY(.85); } 50% { transform: scaleY(1); } }
|
|
131
|
+
@media (max-width: 700px) {
|
|
132
|
+
.commerce-intro__inner { padding-block: 24px; }
|
|
133
|
+
.commerce-intro__masthead p span { display: block; margin: 4px 0 0; font-size: 10px; }
|
|
134
|
+
.commerce-intro__edition { display: none; }
|
|
135
|
+
.commerce-intro.is-enhanced .commerce-intro__stage { min-height: 590px; }
|
|
136
|
+
.commerce-intro.is-enhanced .commerce-intro__scene { grid-template-columns: minmax(0, 1fr); gap: 24px; padding-block: 36px; }
|
|
137
|
+
.commerce-intro__headline,
|
|
138
|
+
.commerce-intro__overview h2 { font-size: clamp(40px, 10vw, 68px); margin-bottom: 18px; }
|
|
139
|
+
.commerce-intro__eyebrow { margin-bottom: 16px; }
|
|
140
|
+
.commerce-intro__description { max-width: 330px; font-size: 14px; }
|
|
141
|
+
.commerce-intro__visual { min-height: 240px; margin-inline: 20px; }
|
|
142
|
+
.commerce-intro__diagram { max-width: 275px; padding: 18px; }
|
|
143
|
+
.commerce-intro__diagram-top { padding-bottom: 12px; }
|
|
144
|
+
.commerce-intro__audit-row { padding-block: 14px; }
|
|
145
|
+
.commerce-intro__orbit { width: 230px; height: 230px; }
|
|
146
|
+
.commerce-intro__scan { top: 46px; }
|
|
147
|
+
.commerce-intro__bars { height: 115px; margin-block: 16px; }
|
|
148
|
+
.commerce-intro__store { padding-block: 16px; }
|
|
149
|
+
.commerce-intro__product { min-height: 110px; }
|
|
150
|
+
.commerce-intro__map { padding-top: 12px; }
|
|
151
|
+
.commerce-intro__map > i { height: 14px; }
|
|
152
|
+
.commerce-intro__map span { padding: 8px; font-size: 10px; }
|
|
153
|
+
.commerce-intro__footer { flex-wrap: wrap; gap: 20px; }
|
|
154
|
+
.commerce-intro__steps { flex-basis: 100%; gap: 10px; }
|
|
155
|
+
.commerce-intro__steps li { font-size: 12px; }
|
|
156
|
+
.commerce-intro__step-number { display: block; margin: 0 0 5px; }
|
|
157
|
+
.commerce-intro__toggle { margin-left: auto; }
|
|
158
|
+
@keyframes commerce-scan { 0%, 100% { transform: translateY(0); opacity: .2; } 50% { transform: translateY(125px); opacity: .75; } }
|
|
159
|
+
}
|
|
160
|
+
/* A readable static overview is also the default without JavaScript. */
|
|
161
|
+
@media (prefers-reduced-motion: reduce) {
|
|
162
|
+
.commerce-intro .commerce-intro__stage { min-height: 350px; }
|
|
163
|
+
.commerce-intro .commerce-intro__scene { display: none; }
|
|
164
|
+
.commerce-intro .commerce-intro__overview { position: static; width: auto; height: auto; padding-block: 50px; clip-path: none; overflow: visible; }
|
|
165
|
+
.commerce-intro *, .commerce-intro *::before { animation: none !important; }
|
|
166
|
+
.commerce-intro .commerce-intro__toggle { display: none; }
|
|
167
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="dark"><title>001 — Commerce Storyboard | CSS Motion Library</title><link rel="stylesheet" href="../../src/demo.css"><link rel="stylesheet" href="animation.css"></head><body><nav class="demo-nav"><a href="../../">← Library</a><span>001 / Commerce Storyboard</span><a href="../002-commerce-film/">Next: Commerce Film ↗</a></nav><main>
|
|
3
|
+
|
|
4
|
+
<section id="intro-video" class="commerce-intro" aria-labelledby="commerce-intro-title" data-commerce-intro>
|
|
5
|
+
<div class="commerce-intro__inner">
|
|
6
|
+
<div class="commerce-intro__masthead">
|
|
7
|
+
<p>Your studio <span>/ Shopify & AI Commerce</span></p>
|
|
8
|
+
<span class="commerce-intro__edition" aria-hidden="true">The approach / 01—04</span>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="commerce-intro__stage" id="commerce-intro-stage">
|
|
11
|
+
<div class="commerce-intro__overview">
|
|
12
|
+
<p class="commerce-intro__eyebrow">A clear path forward</p>
|
|
13
|
+
<h2 id="commerce-intro-title">Audit. Plan.<br><span>Build. Measure.</span></h2>
|
|
14
|
+
<p class="commerce-intro__description">A clear plan for your Shopify store, from the first audit to ongoing improvement.</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="commerce-intro__scene commerce-intro__scene--1" style="--step: 0" aria-hidden="true">
|
|
18
|
+
<div class="commerce-intro__copy">
|
|
19
|
+
<p class="commerce-intro__eyebrow">01 / Audit</p>
|
|
20
|
+
<p class="commerce-intro__headline">Find the<br><span>friction.</span></p>
|
|
21
|
+
<p class="commerce-intro__description">Understand what is holding your Shopify store back.</p>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="commerce-intro__visual commerce-intro__visual--audit">
|
|
24
|
+
<span class="commerce-intro__orbit"></span>
|
|
25
|
+
<div class="commerce-intro__diagram">
|
|
26
|
+
|
|
27
|
+
<div class="commerce-intro__diagram-top"><span>Store audit</span><span>↗</span></div>
|
|
28
|
+
<div class="commerce-intro__audit-row"><span>Storefront</span><i></i></div>
|
|
29
|
+
<div class="commerce-intro__audit-row"><span>Checkout</span><i></i></div>
|
|
30
|
+
<div class="commerce-intro__audit-row"><span>Integrations</span><i></i></div>
|
|
31
|
+
<div class="commerce-intro__scan"></div>
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
<span class="commerce-intro__visual-note">Built around your business</span>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="commerce-intro__scene commerce-intro__scene--2" style="--step: 1" aria-hidden="true">
|
|
39
|
+
<div class="commerce-intro__copy">
|
|
40
|
+
<p class="commerce-intro__eyebrow">02 / Plan</p>
|
|
41
|
+
<p class="commerce-intro__headline">Map the<br><span>next move.</span></p>
|
|
42
|
+
<p class="commerce-intro__description">Turn the findings into a clear architecture plan.</p>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="commerce-intro__visual commerce-intro__visual--plan">
|
|
45
|
+
<span class="commerce-intro__orbit"></span>
|
|
46
|
+
<div class="commerce-intro__diagram">
|
|
47
|
+
|
|
48
|
+
<div class="commerce-intro__diagram-top"><span>Architecture plan</span><span>↗</span></div>
|
|
49
|
+
<div class="commerce-intro__map"><span>Storefront</span><i></i><div><span>Checkout</span><span>Integrations</span></div><i></i><span>Customer experience</span></div>
|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
<span class="commerce-intro__visual-note">Built around your business</span>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="commerce-intro__scene commerce-intro__scene--3" style="--step: 2" aria-hidden="true">
|
|
57
|
+
<div class="commerce-intro__copy">
|
|
58
|
+
<p class="commerce-intro__eyebrow">03 / Build</p>
|
|
59
|
+
<p class="commerce-intro__headline">Build with<br><span>purpose.</span></p>
|
|
60
|
+
<p class="commerce-intro__description">Bring the plan to life across your store and its integrations.</p>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="commerce-intro__visual commerce-intro__visual--build">
|
|
63
|
+
<span class="commerce-intro__orbit"></span>
|
|
64
|
+
<div class="commerce-intro__diagram">
|
|
65
|
+
|
|
66
|
+
<div class="commerce-intro__diagram-top"><span>Store build</span><span>•••</span></div>
|
|
67
|
+
<div class="commerce-intro__store"><div class="commerce-intro__product"></div><div class="commerce-intro__product-copy"><i></i><i></i><i></i><span>Add to cart +</span></div></div>
|
|
68
|
+
<div class="commerce-intro__build-label">Design → Development → Launch</div>
|
|
69
|
+
|
|
70
|
+
</div>
|
|
71
|
+
<span class="commerce-intro__visual-note">Built around your business</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="commerce-intro__scene commerce-intro__scene--4" style="--step: 3" aria-hidden="true">
|
|
76
|
+
<div class="commerce-intro__copy">
|
|
77
|
+
<p class="commerce-intro__eyebrow">04 / Measure</p>
|
|
78
|
+
<p class="commerce-intro__headline">Keep getting<br><span>better.</span></p>
|
|
79
|
+
<p class="commerce-intro__description">Measure what changed. Use the findings to guide what comes next.</p>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="commerce-intro__visual commerce-intro__visual--measure">
|
|
82
|
+
<span class="commerce-intro__orbit"></span>
|
|
83
|
+
<div class="commerce-intro__diagram">
|
|
84
|
+
|
|
85
|
+
<div class="commerce-intro__diagram-top"><span>Measure & improve</span><span>↗</span></div>
|
|
86
|
+
<div class="commerce-intro__bars"><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
|
87
|
+
<div class="commerce-intro__chart-label">Observe → Learn → Refine</div>
|
|
88
|
+
|
|
89
|
+
</div>
|
|
90
|
+
<span class="commerce-intro__visual-note">Illustrative process</span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
</div>
|
|
95
|
+
<div class="commerce-intro__footer">
|
|
96
|
+
<ol class="commerce-intro__steps">
|
|
97
|
+
|
|
98
|
+
<li style="--step: 0"><span class="commerce-intro__step-number" aria-hidden="true">01</span> Audit<span class="commerce-intro__sr-only">: Understand what is holding your Shopify store back.</span></li>
|
|
99
|
+
|
|
100
|
+
<li style="--step: 1"><span class="commerce-intro__step-number" aria-hidden="true">02</span> Plan<span class="commerce-intro__sr-only">: Turn the findings into a clear architecture plan.</span></li>
|
|
101
|
+
|
|
102
|
+
<li style="--step: 2"><span class="commerce-intro__step-number" aria-hidden="true">03</span> Build<span class="commerce-intro__sr-only">: Bring the plan to life across your store and its integrations.</span></li>
|
|
103
|
+
|
|
104
|
+
<li style="--step: 3"><span class="commerce-intro__step-number" aria-hidden="true">04</span> Measure<span class="commerce-intro__sr-only">: Measure what changed. Use the findings to guide what comes next.</span></li>
|
|
105
|
+
|
|
106
|
+
</ol>
|
|
107
|
+
<button class="commerce-intro__toggle" type="button" aria-controls="commerce-intro-stage" hidden>
|
|
108
|
+
<span data-intro-toggle-icon aria-hidden="true">Ⅱ</span>
|
|
109
|
+
<span data-intro-toggle-label>Pause animation</span>
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</section>
|
|
114
|
+
<script>
|
|
115
|
+
(function () {
|
|
116
|
+
var intro = document.querySelector('[data-commerce-intro]');
|
|
117
|
+
if (!intro) return;
|
|
118
|
+
var button = intro.querySelector('.commerce-intro__toggle');
|
|
119
|
+
var label = intro.querySelector('[data-intro-toggle-label]');
|
|
120
|
+
var icon = intro.querySelector('[data-intro-toggle-icon]');
|
|
121
|
+
var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
122
|
+
var userPaused = false;
|
|
123
|
+
var inView = !('IntersectionObserver' in window);
|
|
124
|
+
function update() {
|
|
125
|
+
var reduce = reducedMotion.matches || document.documentElement.getAttribute('data-a11y-reduce-motion') === 'true';
|
|
126
|
+
intro.classList.toggle('is-enhanced', !reduce);
|
|
127
|
+
intro.classList.toggle('is-playing', !reduce && inView && !document.hidden && !userPaused);
|
|
128
|
+
button.hidden = reduce;
|
|
129
|
+
label.textContent = userPaused ? 'Resume animation' : 'Pause animation';
|
|
130
|
+
icon.textContent = userPaused ? '▷' : 'Ⅱ';
|
|
131
|
+
}
|
|
132
|
+
button.addEventListener('click', function () { userPaused = !userPaused; update(); });
|
|
133
|
+
document.addEventListener('visibilitychange', update);
|
|
134
|
+
if (reducedMotion.addEventListener) reducedMotion.addEventListener('change', update);
|
|
135
|
+
else reducedMotion.addListener(update);
|
|
136
|
+
new MutationObserver(update).observe(document.documentElement, { attributes: true, attributeFilter: ['data-a11y-reduce-motion'] });
|
|
137
|
+
update();
|
|
138
|
+
if ('IntersectionObserver' in window) {
|
|
139
|
+
new IntersectionObserver(function (entries) {
|
|
140
|
+
inView = entries[0].isIntersecting;
|
|
141
|
+
update();
|
|
142
|
+
}, { threshold: 0.15 }).observe(intro);
|
|
143
|
+
}
|
|
144
|
+
})();
|
|
145
|
+
</script>
|
|
146
|
+
</main><footer class="demo-footer">24-second CSS loop · Original edition · <a href="animation.css">View CSS</a></footer></body></html>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 002 — Commerce Film
|
|
2
|
+
|
|
3
|
+
A thirty-second CSS film designed to feel closer to an edited motion-graphics video.
|
|
4
|
+
All pictures are HTML, CSS or original SVG; there is no rendered video or animation engine.
|
|
5
|
+
|
|
6
|
+
| Time | Shot | Direction |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| 0–5s | Commerce in Motion | Masked kinetic typography, lens push and a moving ticker |
|
|
9
|
+
| 5–10s | Audit | Full-frame acid yellow, a tilted diagnostics window and a scanning line |
|
|
10
|
+
| 10–15s | Plan | Blueprint camera drift, drawing SVG wires and staggered nodes |
|
|
11
|
+
| 15–20s | Build | A perspective storefront flies into the frame and the camera pushes in |
|
|
12
|
+
| 20–25s | Measure | Tall typography, an orbit and staggered illustrative chart bars |
|
|
13
|
+
| 25–30s | Better Commerce | Closing title on yellow; short hold before the loop |
|
|
14
|
+
|
|
15
|
+
The film uses CSS scene cuts, a recurring wipe, slow camera movement and deliberate title
|
|
16
|
+
holds. Desktop is 16:9; smaller screens recompose into a 3:4 frame rather than cropping.
|
|
17
|
+
The chart is a motion illustration and claims no measured result.
|
|
18
|
+
|
|
19
|
+
## Playback
|
|
20
|
+
|
|
21
|
+
`../../src/player.js` controls CSSAnimation objects with the browser's native API. Controls
|
|
22
|
+
include pause/play, replay, a keyboard-operable seek bar, time display and 0.5×/1×/1.5× speed.
|
|
23
|
+
The shared controller pauses in hidden tabs and offscreen, remembers manual pause, supports
|
|
24
|
+
multiple instances, handles reduced motion and cleans up listeners on `destroy()`.
|
|
25
|
+
|
|
26
|
+
No-JS or reduced-motion mode shows “Better commerce. By design.” and the process summary.
|
|
27
|
+
With reduced motion active, playback controls are hidden and automatic motion stays paused.
|
|
28
|
+
|
|
29
|
+
## Customize
|
|
30
|
+
|
|
31
|
+
Edit text and SVG in `index.html`. Theme variables are `--ink`, `--acid`, `--paper`.
|
|
32
|
+
All shot and detail animations share a 30-second timeline; shot starts use `--shot * 5s`.
|
|
33
|
+
To change the duration or scene count, update the CSS timeline, delays, percentages,
|
|
34
|
+
`data-duration`, and seek-range maximum together.
|
|
35
|
+
|
|
36
|
+
For reuse, copy this HTML and CSS plus `src/player.js`. Adapt the short import in `src/auto.js`
|
|
37
|
+
to your file layout, and keep the demo's box-sizing/font defaults or provide your own.
|
|
38
|
+
Use the existing CSS effect as a reference when adding new shots. Avoid overwriting entry 001.
|