feat(registry): add blend-difference text effect component

This commit is contained in:
Vance Ingalls
2026-05-18 09:15:45 -07:00
parent 3ec0aa456f
commit bed1ca653c
5 changed files with 182 additions and 0 deletions
@@ -0,0 +1,79 @@
---
title: "Blend Difference"
description: "Auto-inverting captions using mix-blend-mode: difference — text flips between white and black per-pixel against the background"
---
# Blend Difference
Auto-inverting captions using mix-blend-mode: difference — text flips between white and black per-pixel against the background
`text` `effect` `blend-mode` `contrast` `inversion`
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add caption-blend-difference
```
</CodeGroup>
## How It Works
White text with `mix-blend-mode: difference` inverts per-pixel against whatever is behind it:
- **Dark background** → text stays white
- **Light background** → text flips to black
- **Color background** → text becomes the complement (blue → orange, red → cyan)
The composition root needs `isolation: isolate` so the blend operates against sibling content (video, images) rather than the page background.
## Variants
| Class | Blend Mode | Effect |
| --- | --- | --- |
| `.blend-difference` | `difference` | Hard per-pixel inversion — maximum contrast |
| `.blend-difference-soft` | `exclusion` | Softer inversion — less harsh on midtones |
| `.blend-difference-screen` | `screen` | Text glows on dark areas, fades on light |
## Example
```html
<div data-composition-id="root" style="isolation: isolate;">
<video src="video.mp4" muted playsinline
data-start="0" data-duration="30" data-track-index="0" />
<div class="clip blend-difference"
data-start="0" data-duration="5" data-track-index="1"
style="position: absolute; inset: 0; z-index: 10;
display: flex; align-items: center; justify-content: center;">
<span style="font-size: 120px; font-weight: 800;">
YOUR CAPTION
</span>
</div>
</div>
```
## CSS Custom Properties
| Property | Default | Description |
| --- | --- | --- |
| `--blend-caption-color` | `white` | Base text color before blending |
| `--blend-mode` | `difference` | Override blend mode on `.blend-difference` |
## Details
| Property | Value |
| --- | --- |
| Type | Component |
## Files
| File | Target | Type |
| --- | --- | --- |
| `caption-blend-difference.html` | `compositions/components/caption-blend-difference.html` | hyperframes:snippet |
## Usage
Open `compositions/components/caption-blend-difference.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+1
View File
@@ -197,6 +197,7 @@
{
"group": "Effects",
"pages": [
"catalog/components/caption-blend-difference",
"catalog/components/grain-overlay",
"catalog/components/grid-pixelate-wipe",
"catalog/components/shimmer-sweep",
@@ -0,0 +1,83 @@
<!--
Blend Difference — auto-inverting captions via mix-blend-mode.
Text color inverts per-pixel against whatever is behind it:
white stays white on dark areas, flips to black on light areas.
On color video, white inverts to the complement (blue → orange,
red → cyan, green → magenta).
Setup:
1. The composition root (or a shared ancestor of both the video
and the caption layer) MUST have `isolation: isolate` so the
blend operates against sibling content, not the page background.
2. Add class="blend-difference" to any caption container.
3. Set caption text color to white. The blend mode handles the rest.
Works on any element — divs, spans, SVG text, even images.
Customize:
- --blend-caption-color: base text color (default white)
- Change blend mode via --blend-mode to 'exclusion' for a softer effect
Variants:
- .blend-difference → standard per-pixel inversion
- .blend-difference-soft → exclusion mode, less harsh contrast
- .blend-difference-screen → text glows on dark, fades on light
-->
<style>
.blend-difference {
mix-blend-mode: var(--blend-mode, difference);
color: var(--blend-caption-color, white);
pointer-events: none;
}
.blend-difference-soft {
mix-blend-mode: exclusion;
color: var(--blend-caption-color, white);
pointer-events: none;
}
.blend-difference-screen {
mix-blend-mode: screen;
color: var(--blend-caption-color, white);
pointer-events: none;
}
</style>
<!--
Composition setup example:
<div data-composition-id="root" ... style="isolation: isolate;">
<video id="bg" data-start="0" data-duration="30" data-track-index="0"
src="video.mp4" muted playsinline></video>
<div class="clip blend-difference" data-start="0" data-duration="5" data-track-index="1"
style="position: absolute; inset: 0; z-index: 10;
display: flex; align-items: center; justify-content: center;">
<span style="font-size: 120px; font-weight: 800; text-transform: uppercase;">
YOUR CAPTION
</span>
</div>
</div>
Timeline integration — animate captions normally, blend mode is passive:
tl.from(".caption", {
y: 50, opacity: 0, duration: 0.6, ease: "expo.out"
}, 0.2);
Notes:
- isolation: isolate on the composition root is REQUIRED.
Without it, blend mode composes against the page background
(usually white or black) and you get no inversion.
- Works with any GSAP animation — the blend composites every frame.
- For caption containers with multiple text elements, apply the
class to the shared parent, not each text element individually.
- On pure black backgrounds, white text stays white (difference
of white and black = white). The effect is most visible when
the background has varied luminance or color.
-->
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-blend-difference",
"type": "hyperframes:component",
"title": "Blend Difference",
"description": "Auto-inverting text using mix-blend-mode: difference — flips between white and black per-pixel against the background",
"tags": ["text", "effect", "blend-mode", "contrast", "inversion"],
"files": [
{
"path": "caption-blend-difference.html",
"target": "compositions/components/caption-blend-difference.html",
"type": "hyperframes:snippet"
}
]
}
+4
View File
@@ -127,6 +127,10 @@
"name": "caption-highlight",
"type": "hyperframes:component"
},
{
"name": "caption-blend-difference",
"type": "hyperframes:component"
},
{
"name": "instagram-follow",
"type": "hyperframes:block"