Deterministic
The same items always draw the same print, in any order, at any size.
Deterministic
The same items always draw the same print, in any order, at any size.
Honest
Mark area is weight, unknown values are drawn rather than dropped, and when items outnumber marks the print says so.
Several things at once
Colour, shape and lightness can each show a different property of the same items.
Small
No runtime dependencies. The layout works without React; the components need React 18 or 19.
It is not device or browser fingerprinting: nothing here identifies anyone.
320 books. Switch views, or choose a legend entry to pick out its books wherever they sit.
combined-view.tsximport { useMemo } from "react";import type { FingerprintAnnotation } from "@data-profile/fingerprint";import { FingerprintCanvas, FingerprintLegend, FingerprintViewSwitch, useFingerprint, type FingerprintView,} from "@data-profile/fingerprint/react";import "@data-profile/fingerprint/styles.css";import { dimensions, library } from "./library";
// One combined view showing three things at once, then one view per dimension.const views: FingerprintView[] = [ { id: "combined", label: "Combined", encoding: { arrange: "genre", color: "genre", shape: "state", lightness: "decade" }, }, { id: "genre", label: "Genre", encoding: { arrange: "genre" } }, { id: "decade", label: "Published", encoding: { arrange: "decade" } }, { id: "state", label: "Reading state", encoding: { arrange: "state" } },];
// Callouts: you write the words; the canvas points at a real mark of the named categories.const annotations: FingerprintAnnotation[] = [ { id: "fiction", dimensionId: "genre", categoryIds: ["fiction"], title: "Mostly fiction", body: "The largest genre forms the core.", }, { id: "unread", dimensionId: "state", categoryIds: ["unread"], title: "A long reading list", body: "Unread books, wherever they sit.", },];
export default function CombinedView() { const items = useMemo(() => library(320), []); const fp = useFingerprint({ items, dimensions, views, unit: "books" }); return ( <div className="demo-split"> <div> <FingerprintViewSwitch views={fp.views} value={fp.viewId} onChange={fp.setViewId} /> <FingerprintCanvas {...fp.canvasProps} title="A library" annotations={annotations} /> </div> <div> {fp.layout.legends.map((group) => ( <FingerprintLegend key={group.dimensionId} {...fp.legendProps(group)} hideEmpty /> ))} </div> </div> );}