polite-media 0.4.2 → 0.4.3
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/CHANGELOG.md +21 -0
- package/README.md +20 -5
- package/dist/image.css +1 -1
- package/dist/image.d.ts.map +1 -1
- package/dist/image.js +20 -1
- package/dist/image.js.map +1 -1
- package/package.json +1 -1
- package/src/image.css +1 -1
- package/src/image.ts +20 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
3
3
|
Notable changes, newest first. Versions follow [semver](https://semver.org); while
|
|
4
4
|
this is `0.x`, a minor bump may still break things and will say so here.
|
|
5
5
|
|
|
6
|
+
## 0.4.3 (2026-09-06)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **An image below the fold arrived without its fade.** The stylesheet's failsafe reveals a marked
|
|
11
|
+
image after `--polite-failsafe` (default `5s`) whatever JavaScript does, and it was firing on
|
|
12
|
+
images `revealImages()` had already claimed. A lazy image that had not loaded yet reached
|
|
13
|
+
`opacity: 1` while still in flight, so when the picture finally arrived there was nothing left to
|
|
14
|
+
fade. Measured on a live page: eleven images revealed that way five seconds in, none of them
|
|
15
|
+
loaded.
|
|
16
|
+
|
|
17
|
+
The library now marks an image it owns with `data-polite-managed` and the stylesheet stands down
|
|
18
|
+
for those. A claimed image is safe without the failsafe because the module always resolves one, on
|
|
19
|
+
`decode()` and failing that on `load` or `error`; if a teardown gives up on a claim, the mark is
|
|
20
|
+
removed and the stylesheet takes the image back.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`data-polite-managed` on an `<img>`**, for as long as `revealImages()` owns its reveal. Part of
|
|
25
|
+
the public CSS API like the other attributes the library writes.
|
|
26
|
+
|
|
6
27
|
## 0.4.2 (2026-09-06)
|
|
7
28
|
|
|
8
29
|
### Fixed
|
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ warmOnIntent('a[data-hero]', (link) => ({
|
|
|
143
143
|
|
|
144
144
|
## The attributes
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
Eight in total. The distinction that catches people is the middle column: two of
|
|
147
147
|
the ones you write are live on their own, and one is inert until you call
|
|
148
148
|
something.
|
|
149
149
|
|
|
@@ -156,6 +156,7 @@ something.
|
|
|
156
156
|
| `data-polite-failed` | the box | written by the library |
|
|
157
157
|
| `data-polite-paused` | `<html>` | written by the library |
|
|
158
158
|
| `data-polite-active` | `<html>` | written by the library |
|
|
159
|
+
| `data-polite-managed` | the `<img>` | written by the library |
|
|
159
160
|
|
|
160
161
|
`data-polite-reveal` is the one to be careful with. `image.css` hides a marked
|
|
161
162
|
image immediately, so marking one you never pass to `revealImages()` leaves it
|
|
@@ -176,7 +177,7 @@ stop something that was never registered, and nothing else lets CSS answer that:
|
|
|
176
177
|
}
|
|
177
178
|
```
|
|
178
179
|
|
|
179
|
-
The bottom
|
|
180
|
+
The bottom five are yours to style against and never to write yourself. They are
|
|
180
181
|
the public CSS API, along with `--polite-fade` and `--polite-failsafe`.
|
|
181
182
|
|
|
182
183
|
### Composing your own animation with the reveal
|
|
@@ -653,9 +654,23 @@ so with scripting off the photos arrive unfaded instead of never arriving. And
|
|
|
653
654
|
because no media query can see a bundle that fails while scripting is on, the
|
|
654
655
|
stylesheet reveals any marked image after `--polite-failsafe` (default `5s`)
|
|
655
656
|
regardless. A missed selector or a dead bundle costs you the fade, not the
|
|
656
|
-
picture.
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
picture.
|
|
658
|
+
|
|
659
|
+
**The failsafe is only for images nothing is going to reveal.** It stands down
|
|
660
|
+
for two kinds: one already revealed, and one `revealImages()` has claimed, which
|
|
661
|
+
the library marks with `data-polite-managed`. Both matter.
|
|
662
|
+
|
|
663
|
+
Leaving it on a revealed image let it, not the reveal, decide `opacity`, because
|
|
664
|
+
an animation outranks every normal declaration in the cascade; Firefox then held
|
|
665
|
+
a decoded image at zero for the full five seconds. Leaving it on a claimed one is
|
|
666
|
+
worse and quieter: a lazy image below the fold reaches `opacity: 1` while it is
|
|
667
|
+
still in flight, so when the picture finally arrives there is no fade left to run.
|
|
668
|
+
Measured on a live page, eleven images were revealed that way five seconds in,
|
|
669
|
+
none of them loaded.
|
|
670
|
+
|
|
671
|
+
A claimed image is safe without it because this module always resolves one: on
|
|
672
|
+
`decode()`, and failing that on `load` or `error`. If a teardown gives up on a
|
|
673
|
+
claim, the mark is removed and the stylesheet takes the image back.
|
|
659
674
|
|
|
660
675
|
## Why it exists
|
|
661
676
|
|
package/dist/image.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@media(scripting:enabled){img[data-polite-reveal]{opacity:0;transition:opacity var(--polite-fade, .35s) ease}img[data-polite-reveal]:not([data-polite-ready]){animation:polite-reveal-failsafe 0s var(--polite-failsafe, 5s) forwards}img[data-polite-reveal][data-polite-ready]{opacity:1}@keyframes polite-reveal-failsafe{to{opacity:1}}}@media(prefers-reduced-motion:reduce){img[data-polite-reveal]{transition:none}}
|
|
1
|
+
@media(scripting:enabled){img[data-polite-reveal]{opacity:0;transition:opacity var(--polite-fade, .35s) ease}img[data-polite-reveal]:not([data-polite-ready]):not([data-polite-managed]){animation:polite-reveal-failsafe 0s var(--polite-failsafe, 5s) forwards}img[data-polite-reveal][data-polite-ready]{opacity:1}@keyframes polite-reveal-failsafe{to{opacity:1}}}@media(prefers-reduced-motion:reduce){img[data-polite-reveal]{transition:none}}
|
package/dist/image.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3D,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3D,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA+FD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,IAAI,CA2E/F;AAKD,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/image.js
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { POLITE_IMAGE_READY } from './events.js';
|
|
19
19
|
import { resolveTargets } from './targets.js';
|
|
20
20
|
const READY = 'data-polite-ready';
|
|
21
|
+
/** Written while this module owns an image's reveal, so image.css can stand down. */
|
|
22
|
+
const MANAGED = 'data-polite-managed';
|
|
21
23
|
function markReady(image) {
|
|
22
24
|
image.setAttribute(READY, '');
|
|
23
25
|
image.dispatchEvent(new CustomEvent(POLITE_IMAGE_READY, {
|
|
@@ -104,8 +106,16 @@ export function revealImages(target, options = {}) {
|
|
|
104
106
|
const controller = new AbortController();
|
|
105
107
|
const { signal } = controller;
|
|
106
108
|
scheduleUnmanagedCheck();
|
|
109
|
+
const claimed = [];
|
|
107
110
|
for (const image of resolveTargets(target)) {
|
|
108
111
|
managed.add(image);
|
|
112
|
+
// Tells the stylesheet to leave this one alone. The failsafe there is for images nothing
|
|
113
|
+
// reveals, and firing it on one this module owns is worse than not firing it at all: opacity
|
|
114
|
+
// reaches 1 while the picture is still in flight, so when it arrives there is no fade left to
|
|
115
|
+
// run. Measured on a real page, eleven below-the-fold images were revealed that way five
|
|
116
|
+
// seconds in, none of them loaded.
|
|
117
|
+
image.setAttribute(MANAGED, '');
|
|
118
|
+
claimed.push(image);
|
|
109
119
|
// Eager images are revealed at once rather than skipped.
|
|
110
120
|
//
|
|
111
121
|
// Skipping looks like the cautious choice and is the opposite: image.css
|
|
@@ -155,7 +165,16 @@ export function revealImages(target, options = {}) {
|
|
|
155
165
|
image.addEventListener('error', () => markReady(image), { once: true, signal });
|
|
156
166
|
});
|
|
157
167
|
}
|
|
158
|
-
|
|
168
|
+
// Hands anything still unrevealed back to the stylesheet. A router tearing the page down before
|
|
169
|
+
// the images resolved would otherwise leave them owned by a module that has stopped listening,
|
|
170
|
+
// and with the failsafe suppressed that is hidden forever rather than merely unfaded.
|
|
171
|
+
return () => {
|
|
172
|
+
controller.abort();
|
|
173
|
+
for (const image of claimed) {
|
|
174
|
+
if (!image.hasAttribute(READY))
|
|
175
|
+
image.removeAttribute(MANAGED);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
159
178
|
}
|
|
160
179
|
// Re-exported so `polite-media/image` carries the ElementEventMap and
|
|
161
180
|
// DocumentEventMap augmentation too: it only reaches a consumer whose program
|
package/dist/image.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,kBAAkB,EAA+B,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAe,MAAM,cAAc,CAAC;AAsB3D,MAAM,KAAK,GAAG,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polite-media",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Background video, image reveals and next-page image warming. Reveals on the frame that actually painted, plays only what is on screen, respects data and motion.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"video",
|
package/src/image.css
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
* Eager images hid the bug, because they are marked ready inside the same task
|
|
55
55
|
* that applies the stylesheet, before this can take effect.
|
|
56
56
|
*/
|
|
57
|
-
img[data-polite-reveal]:not([data-polite-ready]) {
|
|
57
|
+
img[data-polite-reveal]:not([data-polite-ready]):not([data-polite-managed]) {
|
|
58
58
|
animation: polite-reveal-failsafe 0s var(--polite-failsafe, 5s) forwards;
|
|
59
59
|
}
|
|
60
60
|
|
package/src/image.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface RevealImagesOptions {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const READY = 'data-polite-ready';
|
|
43
|
+
/** Written while this module owns an image's reveal, so image.css can stand down. */
|
|
44
|
+
const MANAGED = 'data-polite-managed';
|
|
43
45
|
|
|
44
46
|
function markReady(image: HTMLImageElement): void {
|
|
45
47
|
image.setAttribute(READY, '');
|
|
@@ -142,8 +144,17 @@ export function revealImages(target: ImageTarget, options: RevealImagesOptions =
|
|
|
142
144
|
|
|
143
145
|
scheduleUnmanagedCheck();
|
|
144
146
|
|
|
147
|
+
const claimed: HTMLImageElement[] = [];
|
|
148
|
+
|
|
145
149
|
for (const image of resolveTargets(target)) {
|
|
146
150
|
managed.add(image);
|
|
151
|
+
// Tells the stylesheet to leave this one alone. The failsafe there is for images nothing
|
|
152
|
+
// reveals, and firing it on one this module owns is worse than not firing it at all: opacity
|
|
153
|
+
// reaches 1 while the picture is still in flight, so when it arrives there is no fade left to
|
|
154
|
+
// run. Measured on a real page, eleven below-the-fold images were revealed that way five
|
|
155
|
+
// seconds in, none of them loaded.
|
|
156
|
+
image.setAttribute(MANAGED, '');
|
|
157
|
+
claimed.push(image);
|
|
147
158
|
// Eager images are revealed at once rather than skipped.
|
|
148
159
|
//
|
|
149
160
|
// Skipping looks like the cautious choice and is the opposite: image.css
|
|
@@ -193,7 +204,15 @@ export function revealImages(target: ImageTarget, options: RevealImagesOptions =
|
|
|
193
204
|
});
|
|
194
205
|
}
|
|
195
206
|
|
|
196
|
-
|
|
207
|
+
// Hands anything still unrevealed back to the stylesheet. A router tearing the page down before
|
|
208
|
+
// the images resolved would otherwise leave them owned by a module that has stopped listening,
|
|
209
|
+
// and with the failsafe suppressed that is hidden forever rather than merely unfaded.
|
|
210
|
+
return () => {
|
|
211
|
+
controller.abort();
|
|
212
|
+
for (const image of claimed) {
|
|
213
|
+
if (!image.hasAttribute(READY)) image.removeAttribute(MANAGED);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
// Re-exported so `polite-media/image` carries the ElementEventMap and
|