|
| 1 | +<!-- |
| 2 | + Blend Difference — auto-inverting captions via mix-blend-mode. |
| 3 | +
|
| 4 | + Text color inverts per-pixel against whatever is behind it: |
| 5 | + white stays white on dark areas, flips to black on light areas. |
| 6 | + On color video, white inverts to the complement (blue → orange, |
| 7 | + red → cyan, green → magenta). |
| 8 | +
|
| 9 | + Setup: |
| 10 | + 1. The composition root (or a shared ancestor of both the video |
| 11 | + and the caption layer) MUST have `isolation: isolate` so the |
| 12 | + blend operates against sibling content, not the page background. |
| 13 | + 2. Add class="blend-difference" to any caption container. |
| 14 | + 3. Set caption text color to white. The blend mode handles the rest. |
| 15 | +
|
| 16 | + Works on any element — divs, spans, SVG text, even images. |
| 17 | +
|
| 18 | + Customize: |
| 19 | + - --blend-caption-color: base text color (default white) |
| 20 | + - Change blend mode via --blend-mode to 'exclusion' for a softer effect |
| 21 | +
|
| 22 | + Variants: |
| 23 | + - .blend-difference → standard per-pixel inversion |
| 24 | + - .blend-difference-soft → exclusion mode, less harsh contrast |
| 25 | + - .blend-difference-screen → text glows on dark, fades on light |
| 26 | +--> |
| 27 | + |
| 28 | +<style> |
| 29 | + .blend-difference { |
| 30 | + mix-blend-mode: var(--blend-mode, difference); |
| 31 | + color: var(--blend-caption-color, white); |
| 32 | + pointer-events: none; |
| 33 | + } |
| 34 | + |
| 35 | + .blend-difference-soft { |
| 36 | + mix-blend-mode: exclusion; |
| 37 | + color: var(--blend-caption-color, white); |
| 38 | + pointer-events: none; |
| 39 | + } |
| 40 | + |
| 41 | + .blend-difference-screen { |
| 42 | + mix-blend-mode: screen; |
| 43 | + color: var(--blend-caption-color, white); |
| 44 | + pointer-events: none; |
| 45 | + } |
| 46 | +</style> |
| 47 | + |
| 48 | +<!-- |
| 49 | + Composition setup example: |
| 50 | +
|
| 51 | + <div data-composition-id="root" ... style="isolation: isolate;"> |
| 52 | +
|
| 53 | + <video id="bg" data-start="0" data-duration="30" data-track-index="0" |
| 54 | + src="video.mp4" muted playsinline></video> |
| 55 | +
|
| 56 | + <div class="clip blend-difference" data-start="0" data-duration="5" data-track-index="1" |
| 57 | + style="position: absolute; inset: 0; z-index: 10; |
| 58 | + display: flex; align-items: center; justify-content: center;"> |
| 59 | + <span style="font-size: 120px; font-weight: 800; text-transform: uppercase;"> |
| 60 | + YOUR CAPTION |
| 61 | + </span> |
| 62 | + </div> |
| 63 | + </div> |
| 64 | +
|
| 65 | +
|
| 66 | + Timeline integration — animate captions normally, blend mode is passive: |
| 67 | +
|
| 68 | + tl.from(".caption", { |
| 69 | + y: 50, opacity: 0, duration: 0.6, ease: "expo.out" |
| 70 | + }, 0.2); |
| 71 | +
|
| 72 | +
|
| 73 | + Notes: |
| 74 | + - isolation: isolate on the composition root is REQUIRED. |
| 75 | + Without it, blend mode composes against the page background |
| 76 | + (usually white or black) and you get no inversion. |
| 77 | + - Works with any GSAP animation — the blend composites every frame. |
| 78 | + - For caption containers with multiple text elements, apply the |
| 79 | + class to the shared parent, not each text element individually. |
| 80 | + - On pure black backgrounds, white text stays white (difference |
| 81 | + of white and black = white). The effect is most visible when |
| 82 | + the background has varied luminance or color. |
| 83 | +--> |
0 commit comments