Data display
Graph kit
The frame around a graph engine: a labelled region, zoom controls, legend and node details.
import { GraphRegion, GraphControls, GraphLegend, GraphNodeCard } from '@jsonresume/ui'Source(opens in a new tab)
Examples
Jobs closer to your resume are more similar. Select a job for details.
- Your resume
- Job
- Similarity
Senior Engineer · Stripe
87% similar to your resume
Show code
graph-kit/similarity.tsx
'use client';
import {
GraphControls,
GraphLegend,
GraphNodeCard,
GraphOverlay,
GraphRegion,
Text,
} from '@jsonresume/ui';
import { vars } from '@jsonresume/ui/tokens';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { useState } from 'react';
import * as styles from './similarity.css';
const nodes = [
{ id: 'you', x: 300, y: 160, label: 'Your resume', you: true },
{ id: 'a', x: 190, y: 90, label: 'Senior Engineer · Stripe' },
{ id: 'b', x: 420, y: 110, label: 'Platform Engineer · Fly.io' },
{ id: 'c', x: 230, y: 250, label: 'Staff Frontend · Vercel' },
{ id: 'd', x: 440, y: 240, label: 'Data Engineer · Airbnb' },
];
// The kit frames any graph engine; here a static SVG stands in for it.
export default function GraphKitSimilarity() {
const [zoom, setZoom] = useState(1);
const [selected, setSelected] = useState<string | null>('a');
const node = nodes.find((n) => n.id === selected);
return (
<GraphRegion
label="Job similarity graph"
description="Jobs closer to your resume are more similar. Select a job for details."
height="22rem"
>
<svg
viewBox="0 0 600 340"
className={styles.canvas}
style={assignInlineVars({ [styles.zoom]: String(zoom) })}
>
{nodes.slice(1).map((n) => (
<line
key={n.id}
x1={300}
y1={160}
x2={n.x}
y2={n.y}
className={styles.edge}
/>
))}
{nodes.map((n) => (
<circle
key={n.id}
cx={n.x}
cy={n.y}
r={n.you ? 14 : 10}
className={n.you ? styles.you : styles.job}
data-selected={selected === n.id}
tabIndex={n.you ? undefined : 0}
role={n.you ? undefined : 'button'}
aria-label={n.you ? undefined : n.label}
onClick={() => !n.you && setSelected(n.id)}
onKeyDown={(e) => e.key === 'Enter' && !n.you && setSelected(n.id)}
/>
))}
</svg>
<GraphOverlay corner="top-end">
<GraphControls
onZoomIn={() => setZoom((z) => Math.min(z + 0.25, 2))}
onZoomOut={() => setZoom((z) => Math.max(z - 0.25, 0.5))}
onFit={() => setZoom(1)}
/>
</GraphOverlay>
<GraphOverlay corner="bottom-start">
<GraphLegend
items={[
{ label: 'Your resume', color: vars.color.action.primary },
{ label: 'Job', color: vars.color.action.ink },
{
label: 'Similarity',
color: vars.color.border.strong,
shape: 'line',
},
]}
/>
</GraphOverlay>
{node ? (
<GraphOverlay corner="top-start">
<GraphNodeCard label="Selected job">
<Text weight="semibold">{node.label}</Text>
<Text size="xs" tone="muted">
87% similar to your resume
</Text>
</GraphNodeCard>
</GraphOverlay>
) : null}
</GraphRegion>
);
}graph-kit/similarity.css.ts
import { createVar, style } from '@vanilla-extract/css';
import { vars } from '@jsonresume/ui/tokens';
export const zoom = createVar();
export const canvas = style({
width: '100%',
height: '100%',
transform: `scale(${zoom})`,
transition: `transform ${vars.motion.duration.base} ${vars.motion.easing.standard}`,
});
export const edge = style({
stroke: vars.color.border.strong,
strokeWidth: 1.5,
});
export const you = style({ fill: vars.color.action.primary });
export const job = style({
fill: vars.color.action.ink,
cursor: 'pointer',
outline: 'none',
selectors: {
'&[data-selected="true"]': {
stroke: vars.color.surface.brand,
strokeWidth: 5,
},
'&:focus-visible': { stroke: vars.color.focus, strokeWidth: 4 },
},
});Accessibility
- The region is labelled and described; controls are labelled buttons.
- Node details update politely; make nodes focusable in the engine where possible.
Props
<GraphRegion>
| Prop | Type | Default | Description |
|---|---|---|---|
| children required | ReactNode | – | Overlays (controls, legend, node card) placed in corners with GraphOverlay. |
| label required | string | – | Names the visualization. |
| description | string | – | What the graph shows and how to explore it, for screen readers. |
| height | string | 28rem |
<GraphControls>
| Prop | Type | Default | Description |
|---|---|---|---|
| onFit required | () => void | – | |
| onZoomIn required | () => void | – | |
| onZoomOut required | () => void | – | |
| label | string | Graph view |
<GraphLegend>
| Prop | Type | Default | Description |
|---|---|---|---|
| items required | LegendEntry[] | – | |
| label | string | Legend |
<GraphNodeCard>
| Prop | Type | Default | Description |
|---|---|---|---|
| label required | string | – |