Varendo

CardCollection

Repeated compact records; preserve reading order, headings and bounded loading.

FamilydataLicenseApache-2.0
StandaloneAvailable

Use the web element directly without the semantic runtime.

Semantic bindingAvailable

A registered presentation adapter can bind this component to host-owned meaning and state.

Automatic adaptationStandard recipe

A standard recipe can select this component when its task, bindings, data, and policy are eligible.

Import and live example

Three people in responsive cards with multi-select and an available load-more boundary.

Interactive preview requires JavaScript.

View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
  AeliqoCardCollectionElement,
  AeliqoDeltaElement,
  AeliqoDetailElement,
  AeliqoFilterBuilderElement,
  AeliqoKeyValueElement,
  AeliqoMetricElement,
  AeliqoRecordListElement,
  AeliqoSelectionSummaryElement,
  AeliqoTableElement,
  type AeliqoKeyValueItem,
  type AeliqoTableColumn,
  type AeliqoTableRow,
  } from "@varendo/web/data";

import type {ResultRef,
  Scalar,
} from "@varendo/core";
import type {AeliqoDataColumn, AeliqoDataScope, AeliqoFieldOption} from "@varendo/web/data";

const catalogRef: ResultRef = {
  id: "aeliqo-catalog-example",
  revision: "1",
  outputId: "people",
  queryDigest: "catalog-query",
  scopeDigest: "catalog-scope",
};
const catalogRows: readonly Readonly<Record<string, Scalar>>[] = [
  {id: "ada", name: "Ada Lovelace", team: "Research", date: "2026-09-08", amount: 120},
  {id: "lin", name: "Lin Chen", team: "Product", date: "2026-09-09", amount: 96},
  {id: "grace", name: "Grace Hopper", team: "Research", date: "2026-09-10", amount: 144},
];
const catalogColumns: readonly AeliqoDataColumn[] = [
  {key: "id", label: "ID", type: "text", sortable: true},
  {key: "name", label: "Name", type: "text", sortable: true},
  {key: "team", label: "Team", type: "text"},
  {key: "date", label: "Date", type: "date", sortable: true},
  {key: "amount", label: "Amount", type: "integer", align: "end"},
];
const catalogFields: readonly AeliqoFieldOption[] = [
  {id: "name", label: "Name", type: "text"},
  {id: "team", label: "Team", type: "text"},
  {id: "amount", label: "Amount", type: "integer"},
];
const catalogScope: AeliqoDataScope = {
  loaded: catalogRows.length,
  filteredTotal: catalogRows.length,
  populationDigest: catalogRef.scopeDigest,
  kind: "filtered",
  label: "Authorized people",
};
const tableColumns: readonly AeliqoTableColumn[] = catalogColumns.map(({key, label, sortable, align}) => ({key, label, ...(sortable === undefined ? {} : {sortable}), ...(align === undefined ? {} : {align})}));
const tableRows: readonly AeliqoTableRow[] = catalogRows.map((row) => ({id: row.id ?? null, name: row.name ?? null, team: row.team ?? null, date: row.date ?? null, amount: row.amount ?? null}));
const personKeys = ["string:3:ada", "string:5:grace"] as const;

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) => {
		const element = createCatalogElement<AeliqoCardCollectionElement>("aeliqo-card-collection", root);
		element.title = "People cards";
		element.rows = catalogRows;
		element.columns = catalogColumns;
		element.identity = ["id"];
		element.entity = "person";
		element.headingKey = "name";
		element.selection = "multiple";
		element.hasMore = true;
		element.loadingMore = false;
		element.scope = catalogScope;
	}))(root);

Purpose

Show records as cards with a heading and optional fields in reading order. Selection and Load more are requests the application answers.

When to use it

When to use a different component

Properties and defaults

The host controls selectedKeys, hasMore, and loadingMore; the component never fetches the next page.

Properties, ownership, and initial values
PropertyUse and ownershipDeclared typeInitial value
rowsSupplies the bounded records to render. The component never fetches or widens this set.readonly AeliqoDataRecord[][]
columnsDefines ordered visible fields, labels, formatting, alignment, and supported column interactions.readonly AeliqoDataColumn[][]
identityLists stable identity fields used for selection, detail targeting, and state transfer.readonly string[][]
entityNames the catalog entity whose records and identities are displayed.string'record'
headingKeySupplies heading key to the component as application-owned input.string''
selectionChooses none, single, or multiple selection behavior.AeliqoSelectionMode'none'
selectedKeysControls the current collection selection using stable encoded identities.readonly string[][]
resultSupplies the Result reference that binds displayed data to its query and scope evidence.ResultRef | undefinedundefined
titleProvides the visible title for the current data or compound surface.string''
scopeSupplies the visible authorization and population scope associated with the displayed value or rows.AeliqoDataScope | undefinedundefined
statusSupplies the explicit ready, loading, empty, partial, stale, or error state.AeliqoDataStatus'ready'
messageProvides the user-facing status, error, or recovery message.string''
hasMoreDeclares whether more is available. The host updates it with the underlying data state.booleanfalse
loadingMoreEnables or disables loading more as explicit host-controlled state.booleanfalse
loadLabelProvides the visible text for the load control or section.string'Load more'

Events

Events, payloads, timing, and application responsibilities
EventPayloadEmittedApplication responsibility
aeliqo-card-selectionStable selected identity keys plus result lineage.After card selection changes.Validate scope and identity, then update selectedKeys.
aeliqo-data-load-more{requested: true}After the user requests the next bounded card page.Fetch the next authorized page and update rows, hasMore, and loadingMore.

Events cross the component boundary as user proposals. Validate their detail and current authority before applying a business effect.

Listen for aeliqo-card-selection

TypeScript example
const element = document.querySelector('aeliqo-card-collection');<p>element?.addEventListener('aeliqo-card-selection', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

Listen for aeliqo-data-load-more

TypeScript example
const element = document.querySelector('aeliqo-card-collection');</p>
<p>element?.addEventListener('aeliqo-data-load-more', (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:

Expected result: Cards adapt to available width and emit only identity selection or bounded load-more requests.

Keyboard, focus, and accessibility

Keyboard behavior:

Exposed semantics:

Responsive behavior

Cards adapt to available width. Let field values wrap and keep each selection control reachable.

Style hooks

Generated TypeScript declaration

View generated declaration
export declare class AeliqoCardCollectionElement extends LitElement {
    static readonly aeliqoVersion = "0.1.0";
    static readonly properties: {
        rows: {
            attribute: boolean;
        };
        columns: {
            attribute: boolean;
        };
        identity: {
            attribute: boolean;
        };
        entity: {
            type: StringConstructor;
        };
        headingKey: {
            attribute: string;
            type: StringConstructor;
        };
        selection: {
            type: StringConstructor;
        };
        selectedKeys: {
            attribute: boolean;
        };
        result: {
            attribute: boolean;
        };
        title: {
            type: StringConstructor;
        };
        scope: {
            attribute: boolean;
        };
        status: {
            type: StringConstructor;
        };
        message: {
            type: StringConstructor;
        };
        hasMore: {
            attribute: string;
            type: BooleanConstructor;
        };
        loadingMore: {
            attribute: string;
            type: BooleanConstructor;
        };
        loadLabel: {
            attribute: string;
            type: StringConstructor;
        };
    };
    rows: readonly AeliqoDataRecord[];
    columns: readonly AeliqoDataColumn[];
    identity: readonly string[];
    entity: string;
    headingKey: string;
    selection: AeliqoSelectionMode;
    selectedKeys: readonly string[];
    result: ResultRef | undefined;
    title: string;
    scope: AeliqoDataScope | undefined;
    status: AeliqoDataStatus;
    message: string;
    hasMore: boolean;
    loadingMore: boolean;
    loadLabel: string;
    protected render(): import("lit").TemplateResult<1>;
    private renderCard;
    private readonly requestSelection;
    private readonly loadMore;
    static readonly styles: import("lit").CSSResult[];
}

Version

This page documents the Varendo 0.6 component contract.