polite-media 0.4.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -3,54 +3,58 @@
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.0 (2026-09-05)
6
+ ## 0.4.1 (2026-09-06)
7
+
8
+ ### Fixed
9
+
10
+ - **A lazy image stayed blank in Firefox** until the failsafe revealed it five seconds later,
11
+ unfaded. The failsafe animation applied to every marked image, and an animation outranks normal
12
+ declarations in the cascade whatever their specificity, so it decided `opacity` and
13
+ `img[data-polite-reveal][data-polite-ready]` could not. It is now scoped to images that have not
14
+ been revealed yet.
7
15
 
8
- The first release shaped by putting the library into two real sites rather than
9
- by reading it. Everything here is something that went wrong during that, or
10
- something a stranger could not find.
16
+ Eager images were never affected, because they are marked ready inside the same task that applies
17
+ the stylesheet. That is why the existing test passed in every engine: it covered the eager half,
18
+ and asserted the attribute rather than what the pixels did.
19
+
20
+ ## 0.4.0 (2026-09-05)
11
21
 
12
22
  ### Added
13
23
 
14
24
  - **`data-polite-active` on `<html>`, for as long as any video is registered.**
15
- A pause control is markup on every page and must never offer to stop something
16
- that was never registered, and nothing let CSS answer whether anything was.
17
- Both consumers had independently written the same attribute in their own
18
- namespace, with the same comment explaining why it could not be a
19
- `data-polite-*` one.
20
-
21
- - **A warning when a second `register()` discards its options.** The first
22
- registration stands and the second call's options are dropped, which is right
23
- for a client-side router re-running the same call and wrong when the values
24
- differ: a host believes a gate is in force and the only symptom is a video
25
- starting when it should have waited. Silent for the documented pattern of
26
- gating one video and then `registerAll`-ing the rest.
27
-
28
- - **A warning when `image.css` is not in effect.** `data-polite-reveal` does
29
- nothing without it, and a bundler hides that in one direction only: component
30
- CSS is folded together for a build and served separately in dev, so the fade
31
- works in a build and is missing in dev. Measured on a throwaway element,
32
- because reading computed style off an image mid-reveal forces a style flush
33
- that leaves Firefox holding it blank for the whole failsafe delay.
34
-
35
- - **A landing page and the demos, published.** Sixteen demo pages existed and had
36
- never left the repository. They now reference the build and each other
37
- relatively, so they work under a project site's path prefix as well as at a
25
+ Gives CSS a way to show a pause control only on pages that have a video,
26
+ without writing an attribute of your own:
27
+
28
+ ```css
29
+ [data-polite-pause-control] {
30
+ display: none;
31
+ }
32
+ [data-polite-active] [data-polite-pause-control] {
33
+ display: inline-flex;
34
+ }
35
+ ```
36
+
37
+ - **A warning when a second `register()` call's options are discarded.** The
38
+ first registration stands, so a later call cannot change a gate or a policy.
39
+ Quiet when the options match, which is what a client-side router re-running
40
+ the same call on a surviving element does.
41
+
42
+ - **A warning when `image.css` is not loaded** on a page that marks images with
43
+ `data-polite-reveal`. The attribute has no effect without the stylesheet.
44
+
45
+ - **The demos are published**, alongside a new landing page:
46
+ <https://sixra.github.io/polite-media/>. Demo pages now reference the build
47
+ and each other relatively, so they work under a path prefix as well as at a
38
48
  root.
39
49
 
40
50
  ### Documentation
41
51
 
42
- - **Composing your own animation with the reveal.** Gating on
43
- `data-polite-ready` needs a failsafe of your own: the attribute is written only
44
- by JavaScript, while the one in `image.css` is CSS-only and reveals the image
45
- rather than any wrapper around it. A consumer hit exactly that and left 27
46
- cards blank on a failed bundle. The `content-visibility: auto` limit is
47
- recorded beside it, since a skipped subtree runs no animations at all.
48
-
49
- - **"Why it exists" names what the hand-rolled versions got wrong**, because what
50
- this competes with is not another package, it is the snippet.
52
+ - How to compose an animation of your own with `data-polite-ready`, including
53
+ the failsafe to pair with it, and how a skipped `content-visibility: auto`
54
+ subtree affects the timing.
51
55
 
52
- - The status section no longer claims nothing runs it in production. Two sites
53
- do.
56
+ - The attribute table covers `data-polite-active`, and the status section says
57
+ where the library is in use.
54
58
 
55
59
  ## 0.3.1 (2026-09-02)
56
60
 
package/README.md CHANGED
@@ -686,17 +686,15 @@ most of the web, while every test on Chrome still passes.
686
686
  bfcache restore, and mobile browsers pause video while the tab is hidden and
687
687
  leave it paused on return.
688
688
 
689
- **None of this is hypothetical.** The two sites this was extracted from had each
690
- hand-rolled it, by the same author, and each got it wrong differently. One shipped
691
- no `IntersectionObserver` at all, so its hero decoded for the length of the page,
692
- and warmed the next page with `<link rel="prefetch">`, which Safari ignores and
693
- Firefox aborts. The other cross-faded a one-second dissolve over posters cut from
694
- frame 0, which is a double exposure of a still against a frame that has already
695
- moved, checked `prefers-reduced-motion` once at startup, could not recover from a
696
- refused `play()`, and left twelve background videos in the tab order. Both
697
- autoplayed looping video with no way to stop it, which [WCAG 2.2.2][wcag]
698
- requires. Writing this yourself is not hard; writing it correctly is, and nothing
699
- tells you when you haven't.
689
+ **The details add up.** A hand-rolled version usually starts with an autoplaying
690
+ loop and a poster swap, and the rest arrives one bug at a time: an
691
+ `IntersectionObserver` so a hero stops decoding once it is scrolled past, a
692
+ crossfade that is wrong over a poster cut from frame 0, `prefers-reduced-motion`
693
+ read live rather than once at startup, a recovery path for a refused `play()`,
694
+ `tabindex="-1"` so decorative video stays out of the tab order, a warming
695
+ strategy that works outside Chromium, and a stop control, which
696
+ [WCAG 2.2.2][wcag] requires for anything that loops. Writing this yourself is not
697
+ hard; writing all of it is.
700
698
 
701
699
  ## Status
702
700
 
@@ -713,8 +711,8 @@ rejects with `NotAllowedError`; the library follows MDN's documented remedy of
713
711
  surfacing a control and waiting for a gesture.
714
712
 
715
713
  **`0.x`**, so a minor bump may still change behaviour; `CHANGELOG.md` says when
716
- it does. It runs in production on the two sites it was extracted from, which is
717
- where every fix above came from.
714
+ it does. In production use, and every case it handles is a page on the
715
+ [demo site](https://sixra.github.io/polite-media/).
718
716
 
719
717
  Contributing, and how to run the suite: [CONTRIBUTING.md](CONTRIBUTING.md). MIT.
720
718
 
package/dist/image.css CHANGED
@@ -1 +1 @@
1
- @media(scripting:enabled){img[data-polite-reveal]{opacity:0;transition:opacity var(--polite-fade, .35s) ease;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]){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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polite-media",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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
@@ -36,19 +36,25 @@
36
36
  img[data-polite-reveal] {
37
37
  opacity: 0;
38
38
  transition: opacity var(--polite-fade, 350ms) ease;
39
- /*
40
- * Nothing may stay hidden forever. Marking an image and never passing it to
41
- * revealImages() used to leave it invisible permanently, and so did a bundle
42
- * that never arrived; both now resolve to merely unfaded.
43
- *
44
- * Deliberately universal rather than cancelled for images the library owns.
45
- * An earlier version marked those and set `animation: none`, which reverts
46
- * the filled end state: measured in all three engines, an image the failsafe
47
- * had already revealed jumped back to opacity 0 the moment it was claimed.
48
- * Applying to everything removes that case, and costs nothing, since a
49
- * managed image reaches opacity 1 through `data-polite-ready` long before
50
- * the delay and the two agree on the value.
51
- */
39
+ }
40
+
41
+ /*
42
+ * Nothing may stay hidden forever. Marking an image and never passing it to
43
+ * revealImages() used to leave it invisible permanently, and so did a bundle
44
+ * that never arrived; both resolve to merely unfaded.
45
+ *
46
+ * Scoped to images that still need rescuing, which is load-bearing rather than
47
+ * tidy. An animation outranks every normal declaration in the cascade whatever
48
+ * its specificity, so while this applies it decides `opacity` and the rule
49
+ * below cannot. Measured in Firefox: a lazy image marked ready a task after the
50
+ * stylesheet applied stayed at 0 for the whole delay and then snapped to 1
51
+ * unfaded, while Chromium and WebKit revealed it. Cancelling only the animation
52
+ * on such an image put it at 1 immediately.
53
+ *
54
+ * Eager images hid the bug, because they are marked ready inside the same task
55
+ * that applies the stylesheet, before this can take effect.
56
+ */
57
+ img[data-polite-reveal]:not([data-polite-ready]) {
52
58
  animation: polite-reveal-failsafe 0s var(--polite-failsafe, 5s) forwards;
53
59
  }
54
60