Interview Spread
A magazine interview layout — bold questions in a narrow left column alternating with serif answers in a wide right column, anchored by oversized quotation marks, with a speaker marker sliding along a sidebar rail and turns emerging in sequence as you scroll.
npx shadcn@latest add https://webberui.com/r/interview-spread.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<InterviewSpread />
Installation
npx shadcn@latest add https://webberui.com/r/interview-spread.jsonOr, once registries are configured in components.json, install it as @webberui/interview-spread.
Usage
import {
InterviewSpread,
InterviewTurn,
} from "@/components/ui/interview-spread";
<InterviewSpread interviewer="Editor" interviewee="Designer">
<InterviewTurn role="question">How do you see the role of animation in an interface?</InterviewTurn>
<InterviewTurn role="answer">Animation is not decoration — it is narrative.</InterviewTurn>
<InterviewTurn role="question">And what about rhythm?</InterviewTurn>
<InterviewTurn role="answer">Rhythm is everything. I treat every easing curve as a breath.</InterviewTurn>
</InterviewSpread>;If the interview sits inside a custom scroll container (a modal or a sidebar, say), pass the container ref to container so the rail marker and reveal detection use that container as their scroll basis:
const scrollRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollRef} className="h-[300px] overflow-y-auto">
<InterviewSpread container={scrollRef}>{/* … */}</InterviewSpread>
</div>;Props
InterviewSpread
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Put InterviewTurn children here; they are laid out in order |
interviewer | string | "Q" | The interviewer's label (default speaker for questions) |
interviewee | string | "A" | The interviewee's label (default speaker for answers) |
rail | boolean | true | Whether to show the speaker marker rail on the left |
railWidth | number | 56 | Rail width (px) |
container | RefObject<HTMLElement | null> | — | Ref of a nested overflow scroll container; defaults to the window as the scroll container |
once | boolean | true | Plays the reveal only the first time the element enters the viewport |
aria-label | string | "Interview" | accessible label for the layout |
InterviewTurn
| Prop | Type | Default | Description |
|---|---|---|---|
role | "question" | "answer" | "answer" | Questions get the bold narrow left column, answers the serif wide right column |
speaker | string | — | Overrides the speaker label for this turn; defaults to the container's setting |
quoteMark | boolean | true for answers | Whether to show the oversized quotation mark anchoring the layout |
children | ReactNode | — | The spoken content |
How it works
- Questions are set in bold sans-serif and answers in a serif face, alternating left and right across narrow and wide columns to create the layout rhythm of a magazine interview.
- Each answer is anchored at its top left by an oversized quotation mark (
quoteMark, which can be turned off). - As you scroll, the marker on the left rail slides along its track: its position follows the scroll progress, and an
IntersectionObserverdetects which turn is inside the reading-line band to switch the marker's color and speaker initials. - Turns emerge in sequence via
whileInView, with questions drifting in from the left and answers from the right as they fade in.
Accessibility
- The rail marker is decorative only (
aria-hidden), and the speaker information is also presented as a text label at the start of each turn. - Answers are marked up semantically as
blockquote; the reading order is the natural order of the questions and answers. - The layout container carries
role="group"and anaria-label(defaulting to "Interview", which can be overridden). - When the user has "reduce motion" enabled at the system level, the reveal and rail-sliding animations are disabled and the full static layout is presented directly.
- The rail marker's position measurement and scroll watching all clean up their
ResizeObserver/IntersectionObserverand event listeners on unmount.
Cover Story Hero
A magazine-cover hero composition — the masthead is image-masked and tucked behind the subject, cover lines are arranged in the four corners, the cursor drives parallax, and the cover lines fly off one by one on scroll to make way for the body copy.
Dot Leader Contents
A table of contents in book-index style — dot leaders extend dot by dot from left to right to meet the page number, and a preview card floats out from the end of the row on hover.