The Waveform Is Just Bars
Eight evidence cards. Each one carries a small visual: a waveform for call recordings, a bar chart for ad spend, a calendar grid for bookings, a three-line shimmer for call reviews. None of them touch SVG, canvas, or a charting library. They're all <i> elements with inline height styles inside a flex container.
Here's the waveform:
<span className="prism-waveform" aria-hidden="true">
{[14, 25, 12, 36, 22, 45, 29, 16, 38, 51, 25, 40, 19, 30, 48, 23, 15, 34, 22, 12, 29, 17, 9, 20]
.map((height, i) => <i key={i} style={{ height: `${height}%` }} />)}
</span>
The parent .prism-waveform is a fixed-height flex container with align-items: flex-end. Each <i> is a narrow bar. Setting height: ${height}% on the child means "this bar is X percent of the container's height." No coordinate system, no viewBox, no path math. Twenty-four numbers produce a waveform that looks like a real call recording.
The mini bar chart works the same way, with ten bars trending upward: [25, 39, 32, 58, 45, 72, 63, 86, 75, 100]. CSS handles the color, the gap between bars, and the border radius on the tops. The data array carries the shape.
The booking calendar is slightly different. Instead of height percentages, it uses a data-booked attribute:
{Array.from({length: 14}, (_, i) => (
<i key={i} data-booked={i < 8}>
{i < 8 ? <Check size={10} /> : null}
</i>
))}
Fourteen cells. The first eight are booked, the rest empty. data-booked="true" gets one color in CSS, data-booked="false" gets another. This is the same data-attribute-as-CSS-state pattern used elsewhere in the Prism hero for dimming and open states. The booking cell is its own small instance of it.
Every one of these visuals carries aria-hidden="true". That's the correct call. The waveform doesn't tell a screen reader user anything the card text doesn't already say: the metric field says "00:09", the caption says "wrong number", the summary says what the caller said. The waveform is a visual echo of information already present. Marking it hidden removes a noise read with no information loss.
This pattern is right for thumbnail visuals and wrong for anything that carries unique information. A real waveform player where the shape tells you where the conversation peaked, or a bar chart whose exact values matter, should use SVG or canvas with proper axis labels and focus management. But these cards exist to give sighted users a quick register of "this is call data" or "this is spend data" before they read the headline. For that job, a flex column of <i> elements is enough.
The tradeoff is explicit rather than accidental. No hover states, no tooltips, no data labels. The detail lives in the evidence panel when the user opens a card, not in the thumbnail. Keeping the thumbnail dumb keeps the component small.
The numbers in those arrays are hand-authored. The waveform heights were picked to look like a short wrong-number call (flat with a couple of spikes) without actually representing real audio. The spend chart rises left to right to signal an upward cost trend. The shape communicates before the reader reaches the text.