Duotone Image Filter
On hover, grayscale + contrast desaturates the content and a mix-blend-mode: color gradient overlay tints it in two tones — any visual as duotone, pure CSS.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
On hover of .duotone-container, two CSS rules fire simultaneously. .duotone-content receives filter: grayscale(1) contrast(1.2): all pixels are converted to greyscale, then contrast is amplified to widen the gap between light and dark areas — the raw material for a duotone.
The overlay .duotone-overlay, positioned absolute with inset: 0, transitions from opacity: 0 to 1. It carries mix-blend-mode: color and background: linear-gradient(135deg, #6366f1, #ec4899). This blend mode applies the hue and saturation of the gradient layer to the luminance of the desaturated content below: light tones take the color of one gradient end, dark tones the other — the photographic duotone effect.
The two transitions are independent: transition: filter .6s on .duotone-content and transition: opacity .6s on .duotone-overlay. pointer-events: none on the overlay ensures it never captures mouse events. No JavaScript is involved: everything relies on the CSS cascade and the :hover pseudo-class.
Accessibility
- No
@media (prefers-reduced-motion)in the code — thefilterandopacitytransitions (0.6 s) still run even when the OS requests reduced motion. Add this manually based on your audience. - The effect relies solely on
:hover— no:focusor:focus-withinpseudo-class. Keyboard users cannot trigger the duotone in its current form. mix-blend-mode: colorchanges displayed colors but not the semantic structure: screen readers access text and ARIA roles normally, independently of the effect.- The
.duotone-containercarries noroleoraria-label— if the visual conveys information, addrole="img" aria-label="…". - The
.duotone-label(white text on dark background) meets WCAG AA at rest. Under the active color overlay, verify the contrast ratio with your specific duotone colors.
Browser compatibility
Requires CSS filter level 1 and mix-blend-mode — both supported in modern browsers since 2016–2017. Zero external dependencies.
On Edge < 79 (no <code>mix-blend-mode</code>), content turns greyscale on hover but without a color tint — the overlay renders as an opaque solid rectangle. On IE 11, <code>filter</code> is not supported: hover has no visual effect. No JS error in either case.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="duotone-container">
<div class="duotone-content">
<!-- Insert here: img, video, CSS shapes, SVG… -->
</div>
<div class="duotone-overlay"></div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| background on .duotone-overlay | linear-gradient(135deg, #6366f1, #ec4899) | The two duotone colors and the gradient angle. Replace hex values with your brand colors; change the angle (0deg → 180deg) for direction. |
| mix-blend-mode on .duotone-overlay | color | Blend mode. color = hue+saturation from the layer onto the luminance below. Alternatives: hue (without saturation), multiply (darkens), overlay (strong contrast). |
| grayscale() in filter on :hover | grayscale(1) | Desaturation level from 0 to 1. Reduce to 0.7–0.9 for a partial duotone that lets original colors show through. |
| contrast() in filter on :hover | contrast(1.2) | Contrast boost. Raise to 1.4–1.5 for deep blacks and bright whites; lower to 1.0 for a neutral effect. |
| opacity on .duotone-container:hover .duotone-overlay | 1 | Color tint intensity. Reduce to 0.6–0.8 for a subtle tint that lets original colors show through beneath. |
| transition-duration on filter and opacity | .6s | Hover transition length. .3s = snappy, 1s = slow fade. Update both values to keep them in sync. |
FAQ
<img>, <video>, <svg>, or HTML block inside .duotone-content — the CSS filter applies to the entire rendered subtree. Caveat: cross-origin images without a CORS header may not be filtered in some browsers (canvas/CSS security restriction). Content inside an <iframe> is also inaccessible to the parent CSS.:hover. For keyboard accessibility, add :focus-within in parallel: .duotone-container:hover .duotone-content, .duotone-container:focus-within .duotone-content { filter: grayscale(1) contrast(1.2) } — and make the container focusable with tabindex="0" or ensure an interactive element (link, button) is inside..duotone-overlay: background: #6366f1. Light pixels take that hue, dark pixels stay dark (mix-blend-mode: color preserves luminance). For a saturated monochrome with mix-blend-mode: multiply, the overlay background must be white — light areas stay white, dark areas take the tint.