Duotone Plate Hero
A real two-colour separation via SVG filters — with the sRGB interpolation flag that everyone leaves out.
- Category
- Heroes
- Added
- 2026-09-21
- Deps
- none
- Updated
- 2026-09-21
This is a viberdy Pro component
The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.
BLACKHORSE YARD
Twelve rooms, one loading bay, and a licence until four.
Props
"use client";
import { useId } from "react";
/** Hex to the 0..1 channel triple an feFunc table wants. */
function chan(hex: string): [number, number, number] {
const h = hex.replace("#", "");
const n = parseInt(h.length === 3 ? h.split("").map((c) => c + c).join("") : h, 16);
return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
}
export function DuotonePlateHero({
headline = "BLACKHORSE YARD",
standfirst = "Twelve rooms, one loading bay, and a licence until four.",
image = "https://images.unsplash.com/photo-1470225620780-dba8ba36b745?auto=format&fit=crop&w=1600&q=70",
shadow = "#0b1b3f",
highlight = "#d6ff00",
grain = true,
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this heroe
The duotone people usually ship is a chain of CSS filters — sepia, saturate, hue-rotate — tuned by eye until it looks approximately right for one image. It clips, it cannot reach arbitrary colour pairs, and it drifts between browsers. A real duotone is two SVG filter primitives: collapse the image to a single tone with a colour matrix carrying Rec.709 weights on all three output rows, then remap that tone with a component transfer whose table has exactly TWO entries per channel — the shadow colour, then the highlight. Two entries is the whole separation: a straight ramp from one colour to the other. The detail that decides whether it matches the palette you chose is a single attribute. SVG filters compute in linearRGB unless told otherwise, so without forcing sRGB interpolation the result comes out markedly darker and more contrasty than the two colours picked, with nothing in the code to suggest why — it is the most surprising default in the whole filter spec. Two smaller traps are handled too: the filter id comes from useId so a second duotone on the same page cannot silently inherit the first one's colours, and the defs svg is sized to nothing rather than hidden with display none, which has historically stopped filter references resolving at all.
Curator’s note
Leave out colorInterpolationFilters="sRGB" and the duotone comes out darker than the colours you picked, with nothing to tell you why.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Edge Sliver Poster Hero
heroesA saturated poster hero whose photographic sliver opens on hover, with live ticket tiers and a countdown.
Type Specimen Hero
heroesA magazine-cover hero where the photograph inverts through the letterforms, pinned to its own stacking context.
Stacked Type Hero
sectionsOversized, tightly-stacked headline words that reveal in on mount — big type as sculpture.