Skip to content

Duotone Plate Hero

A real two-colour separation via SVG filters — with the sRGB interpolation flag that everyone leaves out.

Proeditorialbrutalistneon
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.

Preview

BLACKHORSE YARD

Twelve rooms, one loading bay, and a licence until four.

Props

Grain
No runtime dependencies
"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
  // ---------------------------------------------------------------
}
Notes

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.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

Featured
NewPro

A saturated poster hero whose photographic sliver opens on hover, with live ticket tiers and a countdown.

heropostereditorial
Featured
NewPro

A magazine-cover hero where the photograph inverts through the letterforms, pinned to its own stacking context.

herotypeblend
Featured

Oversized, tightly-stacked headline words that reveal in on mount — big type as sculpture.

herotypographyeditorial