Hanging Quote Section
A pull quote with the opening mark optically aligned — measured from the real glyph, not a guessed em value.
- Category
- Sections
- 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.
“We design the parts people actually touch. The hinge, the handle, the moment a thing answers back.”
Props
"use client";
import { useEffect, useId, useRef, useState } from "react";
export function HangingQuoteSection({
quote = "We design the parts people actually touch. The hinge, the handle, the moment a thing answers back.",
attribution = "Ana Reis",
role = "Founder, Blackhorse Yard",
}: {
quote?: string;
attribution?: string;
role?: string;
}) {
const cls = "hq-" + useId().replace(/:/g, "");
const measureRef = useRef<HTMLSpanElement>(null);
const [advance, setAdvance] = useState(0);
useEffect(() => {
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this section
Optical alignment is the detail that separates typeset copy from typed copy: an opening quotation mark carries so much white space inside its own box that leaving it in the text block makes the left edge look broken, so it hangs into the margin instead. The usual implementation hardcodes a negative indent of about four tenths of an em, which is correct for exactly one typeface at one weight — the advance of an opening quote varies enormously between faces, and again between the curly and the straight form. Measuring the actual character, rendered in the same face, size and weight, and feeding the result to a custom property makes it right for whatever font the page ends up using, and re-measuring once webfonts resolve fixes the case where the first measurement belonged to a fallback. Two smaller decisions matter as much. The offset is applied with text-indent rather than a negative margin, because text-indent moves the first line only, which is precisely what hanging a quote requires, while a margin drags the whole block and every line under it. And the native hanging-punctuation property — still Safari-only — is layered in with a feature query rather than a JavaScript test, both because the two must never apply together and double the hang, and because a JS branch would render differently on the server than on the client.
Curator’s note
A hardcoded -0.4em is right for exactly one typeface. Measure the glyph you are actually rendering.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Sticky Index Section
sectionsAn editorial index with sticky year headers, and a switch that demonstrates the overflow ancestor that breaks them.
Stacked Type Hero
sectionsOversized, tightly-stacked headline words that reveal in on mount — big type as sculpture.
Ticker Tape Background
backgroundsOversized, near-invisible ticker rows scrolling behind the fold as ambient texture.