Matrix
Entity-feature comparison preserving row/column association and useful comparison at narrow width.
Use the web element directly without the semantic runtime.
A registered presentation adapter can bind this component to host-owned meaning and state.
The standard recipe resolver does not select this component automatically.
Import and live example#
A temporal matrix of person, team, and amount columns.
Interactive preview requires JavaScript.
View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
AeliqoAreaElement,
AeliqoBarElement,
AeliqoCalendarGridElement,
AeliqoHeatmapElement,
AeliqoHistogramElement,
AeliqoMatrixElement,
AeliqoRelationshipElement,
AeliqoScatterElement,
AeliqoTimelineElement,
AeliqoTrendElement,
AeliqoTreeElement,
AeliqoTreemapElement,
} from "@varendo/web/visualization";
import type {Catalog,
PlotUnit,
Result,
ResultRef,
Scalar,
} from "@varendo/core";
import type {VisualizationBindingContext, VisualizationSpec} from "@varendo/core/visualization";
import type {VisualizationDataset} from "@varendo/web/visualization";
const catalogRef: ResultRef = {
id: "aeliqo-catalog-example",
revision: "1",
outputId: "people",
queryDigest: "catalog-query",
scopeDigest: "catalog-scope",
};
const rows: readonly Readonly<Record<string, Scalar>>[] = [
{id: "ada", name: "Ada Lovelace", team: "Research", date: "2026-09-08", amount: 120, low: 80, high: 100, zero: 0},
{id: "lin", name: "Lin Chen", team: "Product", date: "2026-09-09", amount: 96, low: 100, high: 140, zero: 0},
{id: "grace", name: "Grace Hopper", team: "Research", date: "2026-09-10", amount: 144, low: 140, high: 160, zero: 0},
];
const text = {value: "text", nullable: false} as const;
const integer = {value: "integer", nullable: false} as const;
const date = {value: "date", nullable: false, temporal: {calendar: "gregory", grain: "day"}} as const;
const meaning = {
id: "amount",
revision: "1",
label: "Amount",
explanation: "Authorized additive amount",
output: integer,
implementation: {kind: "host-capability", capability: {id: "amount", revision: "1"}},
dependencies: [],
functionRegistryDigest: "catalog-functions",
origin: "manual",
lifecycle: "active",
scope: "workspace",
authority: "reviewed",
aggregation: "additive",
aggregationDimensions: [],
missingPolicy: "reject",
} as const;
const peopleEntity: Catalog["entities"][number] = {
id: "people",
label: "People",
identity: ["id"],
rowGrain: ["id"],
fields: [
{id: "id", label: "ID", role: "identity", type: text},
{id: "name", label: "Name", role: "attribute", type: text},
{id: "team", label: "Team", role: "dimension", type: text},
{id: "date", label: "Date", role: "time", type: date},
{id: "amount", label: "Amount", role: "measure", type: integer, derivation: {id: "amount", revision: "1"}},
{id: "low", label: "Bin start", role: "dimension", type: integer},
{id: "high", label: "Bin end", role: "dimension", type: integer},
{id: "zero", label: "Baseline", role: "measure", type: integer},
],
};
const catalog: Catalog = {
version: "1",
revision: "catalog-1",
functionRegistryDigest: "catalog-functions",
entities: [peopleEntity],
relationships: [],
meanings: [meaning],
capabilities: [],
};
const catalogResult: Result = {
version: "1",
ref: catalogRef,
taskId: "catalog-example-task",
identity: ["id"],
rowGrain: ["id", "date", "team", "name", "low", "high"],
fields: peopleEntity.fields,
counts: {loaded: rows.length, population: {kind: "unknown"}},
precision: {kind: "exact"},
coverage: {kind: "complete", populationDigest: catalogRef.scopeDigest},
consistency: {kind: "snapshot", snapshotId: "catalog-snapshot", sourceRevisions: {people: "1"}},
evidence: {kind: "computed", queryDigest: catalogRef.queryDigest, definitions: []},
filters: [],
warnings: [],
lineage: [],
};
const plot = (mark: PlotUnit["mark"], encoding: PlotUnit["encoding"]): PlotUnit => ({kind: "unit", mark, result: catalogRef, missing: "gap", encoding});
const catalogVisualizationSpecs: Readonly<Record<"trend" | "bar" | "area" | "scatter" | "histogram" | "heatmap", VisualizationSpec>> = {
trend: {version: "1", view: "trend", plot: {version: "1", root: plot("line", {x: {field: "date", scale: "temporal"}, y: {field: "amount", scale: "linear"}})}},
bar: {version: "1", view: "bar", plot: {version: "1", root: plot("bar", {x: {field: "team", scale: "ordinal"}, y: {field: "amount", scale: "linear", zero: true}, series: {field: "name", scale: "ordinal"}})}},
area: {version: "1", view: "area", plot: {version: "1", root: plot("area", {x: {field: "date", scale: "temporal"}, y: {field: "amount", scale: "linear", zero: true}})}, meaning: {id: "amount", revision: "1"}, stack: "none"},
scatter: {version: "1", view: "scatter", plot: {version: "1", root: plot("point", {x: {field: "amount", scale: "linear"}, y: {field: "amount", scale: "linear"}})}},
histogram: {version: "1", view: "histogram", plot: {version: "1", root: plot("rect", {x: {field: "low", scale: "linear"}, x2: {field: "high", scale: "linear"}, y: {field: "amount", scale: "linear", zero: true}, y2: {field: "zero", scale: "linear", zero: true}})}, bins: {start: "low", end: "high", value: "amount", measure: "count", boundary: "start-inclusive-end-exclusive"}},
heatmap: {version: "1", view: "heatmap", plot: {version: "1", root: plot("cell", {x: {field: "team", scale: "ordinal"}, y: {field: "name", scale: "ordinal"}, color: {field: "amount", scale: "linear"}})}},
};
const catalogTemporalSpecs: Readonly<Record<"matrix" | "timeline" | "calendar-grid", VisualizationSpec>> = {
matrix: {version: "1", view: "matrix", result: catalogRef, columns: ["name", "team", "amount"]},
timeline: {version: "1", view: "timeline", result: catalogRef, start: "date"},
"calendar-grid": {version: "1", view: "calendar-grid", result: catalogRef, date: "date"},
};
const catalogVisualizationDataset: VisualizationDataset = {result: catalogRef, rows};
const catalogVisualizationContext: VisualizationBindingContext = {results: [catalogResult], catalog};
const histogramSpec = catalogVisualizationSpecs.histogram as Extract<VisualizationSpec, {view: "histogram"}>;
const histogramContext: VisualizationBindingContext = {...catalogVisualizationContext, histograms: [{result: catalogRef, bins: histogramSpec.bins}]};
const hierarchyRef: ResultRef = {...catalogRef, id: "aeliqo-catalog-hierarchy", outputId: "nodes"};
const hierarchyRows: readonly Readonly<Record<string, Scalar>>[] = [
{id: "company", parent: null, label: "Company", amount: 3},
{id: "research", parent: "company", label: "Research", amount: 2},
{id: "product", parent: "company", label: "Product", amount: 1},
];
const hierarchyResult: Result = {
...catalogResult,
ref: hierarchyRef,
taskId: "catalog-hierarchy-task",
identity: ["id"],
rowGrain: ["id"],
fields: [
{id: "id", label: "ID", role: "identity", type: text},
{id: "parent", label: "Parent", role: "attribute", type: {...text, nullable: true}},
{id: "label", label: "Label", role: "attribute", type: text},
{id: "amount", label: "Amount", role: "measure", type: integer, derivation: {id: "amount", revision: "1"}},
],
counts: {loaded: hierarchyRows.length, population: {kind: "unknown"}},
};
const hierarchyDataset: VisualizationDataset = {result: hierarchyRef, rows: hierarchyRows};
const hierarchySpec: VisualizationSpec = {version: "1", view: "tree", result: hierarchyRef, node: ["id"], parent: ["parent"], label: "label"};
const treemapSpec: VisualizationSpec = {...hierarchySpec, view: "treemap", value: "amount", meaning: {id: "amount", revision: "1"}};
const hierarchyContext: VisualizationBindingContext = {results: [hierarchyResult], catalog};
const relationshipRef: ResultRef = {...catalogRef, id: "aeliqo-catalog-relationship", outputId: "edges"};
const relationshipRows: readonly Readonly<Record<string, Scalar>>[] = [
{edge: "e1", source: "research", target: "company"},
{edge: "e2", source: "product", target: "company"},
];
const relationshipResult: Result = {
...hierarchyResult,
ref: relationshipRef,
identity: ["edge"],
rowGrain: ["edge"],
fields: [
{id: "edge", label: "Edge", role: "identity", type: text},
{id: "source", label: "Source", role: "dimension", type: text},
{id: "target", label: "Target", role: "dimension", type: text},
],
counts: {loaded: relationshipRows.length, population: {kind: "unknown"}},
};
const relationship: Catalog["relationships"][number] = {
id: "reports-to",
revision: "1",
sourceEntity: "people",
targetEntity: "people",
keys: [{sourceField: "id", targetField: "id"}],
cardinality: "many-to-one",
optional: false,
joinPolicy: "validated",
};
const relationshipDataset: VisualizationDataset = {result: relationshipRef, rows: relationshipRows};
const relationshipSpec: VisualizationSpec = {version: "1", view: "relationship", result: relationshipRef, source: ["source"], target: ["target"], relationship: {id: "reports-to", revision: "1"}};
const relationshipContext: VisualizationBindingContext = {
results: [relationshipResult],
catalog: {...catalog, relationships: [relationship]},
relationships: [{result: relationshipRef, relationship: {id: "reports-to", revision: "1"}, source: ["source"], target: ["target"]}],
};
type CatalogVisualizationElement = HTMLElement & {
visualization: VisualizationSpec | undefined;
context: VisualizationBindingContext;
datasets: readonly VisualizationDataset[];
label: string;
width: number;
height: number;
maxMarks: number;
selectionEnabled: boolean;
};
function configure<T extends CatalogVisualizationElement>(element: T, visualization: VisualizationSpec, context: VisualizationBindingContext, datasets: readonly VisualizationDataset[] = [catalogVisualizationDataset]): T {
element.visualization = visualization;
element.context = context;
element.datasets = datasets;
element.label = "Sample people amounts";
element.width = 640;
element.height = 360;
element.maxMarks = 500;
element.selectionEnabled = true;
return element;
}
registerAeliqoElements();
const host = document.querySelector<HTMLElement>("#aeliqo-example") ?? (() => {
const created = document.createElement("div");
created.id = "aeliqo-example";
(document.body ?? document.documentElement).append(created);
return created;
})();
function createCatalogRoot(container: HTMLElement): HTMLElement {
const root = document.createElement("div");
root.dataset.catalogSourceRoot = "true";
root.style.display = "grid";
root.style.gap = "12px";
container.append(root);
return root;
}
function createCatalogElement<T extends HTMLElement>(tagName: string, container: HTMLElement): T {
const element = document.createElement(tagName) as T;
element.setAttribute("data-aeliqo-theme", "light");
container.append(element);
return element;
}
function appendSlottedText(root: HTMLElement, slot: string, textContent: string): void {
const content = document.createElement("span");
content.slot = slot;
content.textContent = textContent;
root.append(content);
}
const root = createCatalogRoot(host);
(((root) => configure(createCatalogElement<AeliqoMatrixElement>("aeliqo-matrix", root), catalogTemporalSpecs.matrix, catalogVisualizationContext)))(root);
Purpose#
Compare records across the declared feature columns in a native table. Exact cell values remain available; include an identity field in the columns when readers need it visible.
When to use it#
- Compare entities across several fields side by side.
- Keep each cell tied to its row and column.
- Show exact values rather than color-encoded cells.
When to use a different component#
- Use Heatmap when color should summarize cell values.
- Use Relationship for declared edges between entities.
Properties and defaults#
Column IDs come from the temporal spec and rows remain tied to the supplied result.
| Property | Use and ownership | Declared type | Initial value |
|---|---|---|---|
visualization | Supplies the validated visualization specification to render. | VisualizationSpec | undefined | undefined |
context | Supplies the validated catalog and result context used to bind the visualization specification. | VisualizationBindingContext | { results: [] } |
datasets | Supplies rows keyed to the declared Result references. The component does not fetch or expand them. | readonly VisualizationDataset[] | [] |
selectedIdentity | Controls the selected visualization identity and keeps it tied to the current Result. | string | '' |
selectionEnabled | Enables mark selection proposals for the visualization. | boolean | true |
label | Provides the visible label and accessible name for the component. | string | 'Data visualization' |
width | Sets the visualization layout width before measured container geometry is applied. | number | 640 |
height | Sets the visualization layout height. The chart still computes mark geometry from its measured container. | number | 320 |
maxMarks | Limits rendered visualization marks. Results above this boundary must be reduced or rejected explicitly. | number | 1000 |
Events#
| Event | Payload | Emitted | Application responsibility |
|---|---|---|---|
aeliqo-visualization-select | Selected mark identity plus Result lineage. | After a selectable mark is activated. | Validate lineage and update controlled visualization or linked-detail selection. |
Events cross the component boundary as user proposals. Validate their detail and current authority before applying a business effect.
Listen for aeliqo-visualization-select
const element = document.querySelector('aeliqo-matrix');<p>element?.addEventListener('aeliqo-visualization-select', (event) => {
const proposal = (event as CustomEvent<unknown>).detail;
// Validate the proposal, update host state, then assign controlled properties.
applyProposal(proposal);
});States and failure handling#
Relevant states:
- ready
- partial
- empty
- error
Expected result: The matrix exposes the declared temporal columns without changing grain.
Keyboard, focus, and accessibility#
Keyboard behavior:
- Tab reaches the scrollable graphic when present and the native row-selection buttons; screen readers can navigate the labelled data table.
- Enter or Space activates the focused selection button.
- Visual marks do not form an arrow-key grid; use the exact-value table to inspect and select rows.
Exposed semantics:
- aria-label
- aria-pressed
- button
- caption
- figcaption
- figure
- nav
- role
- section
- svg
- table
- tbody
- td
- th
- thead
- tr
Responsive behavior#
At container widths of 30rem or less, the matrix keeps readable minimum column widths and scrolls horizontally inside its exact-data region. The surrounding page does not need to scroll sideways.
Style hooks#
- Shadow parts:
data,scope,scroll-hint,viewport - Varendo design tokens referenced:
--aeliqo-color-accent,--aeliqo-color-border,--aeliqo-color-focus,--aeliqo-color-surface,--aeliqo-color-text,--aeliqo-control-border-width
Performance limits#
- Public workload controls: `maxMarks` (default 1000).
Related components#
Generated TypeScript declaration#
View generated declaration
export declare class AeliqoMatrixElement extends AeliqoTemporalElement {
protected readonly view: 'matrix';
}Version#
This page documents the Varendo 0.6 component contract.
