Skip to content

CardGrid

<caos-card-grid>

the CARD body of a list surface — a compact card per record

May be placed on a record page, an app’s home page and a page inside an app.

Name Attribute Description Type Default Required
items property only The cards. Each one an id, the record title, optionally the route it opens (href), a segment or type sub-line, and up to two labelled count/rollup metrics. A card may also carry its leading MARK: icon, a name from the platform glyph vocabulary standing for the record’s kind, or avatar ({ src, name }), a picture of the record itself — a person’s photograph, an organisation’s logo. The avatar wins where both are given, and a card that says neither draws the generic record glyph, which is what every card drew before. Already access-filtered and formatted by the app layer — nothing is fetched here. Set as a property; an attribute cannot hold a list. json none declared Optional
batchSize property only How many cards are painted per batch. The rest are appended as the end of the grid scrolls into view, so a long list does not paint all at once and nothing is dropped. Settable ONLY as a property (the component observes no attributes), and clamped to at least 1. number 24 Optional
Name When it fires What it carries
caos-card-open A card is activated, by click or by keyboard — each card is a real button. { id } — the record the card stands for. The host routes it; the grid does not navigate.

Nothing goes inside this piece.

Design token What it controls
--caos-font The typeface of a card.
--caos-color-surface The ground of a card at rest.
--caos-color-border The hairline around a card at rest.
--caos-radius-lg The corners of a card.
--caos-shadow-card The elevation of a card.
--caos-color-accent The record name, the icon tile, and the border a card takes on hover.
--caos-accent-wash The tint a card fills with on hover.
--caos-focus-ring The ring on a focused card.
--caos-radius-sm The corners of the object icon tile.
--caos-color-text-ghost The sub-line under the name, and the trailing caret.
--caos-color-text-muted A metric’s label, and the sentence shown when the grid is empty.
--caos-color-text A metric’s figure.
--caos-space-4 The gutter between cards, the padding inside a card, and the gap between metrics.
--caos-space-3 The gap between a card’s head and its metrics, and between the icon tile and the name.
--caos-space-2 The padding around the sentence shown when the grid is empty.
--caos-space-1 The gap between the name and its sub-line, and between a metric’s figure and its label.

Nothing else drives this piece by calling it.

Nothing on this piece can be read back.

Part Which piece of it When it is there
::part(grid) the grid the cards are laid out in Always
::part(card) one card Always

A list that is BROWSED rather than compared, where the record name is what somebody is looking for and one or two counts are enough context. It is also the list body that survives a narrow screen best, because cards reflow where a table has to scroll sideways.

  • DataTable — The same field is being read across records and the values need lining up in columns — or rows need selecting, sorting or totalling, none of which a card can do.
  • KanbanBoard — The question is where the work is: the same records stood up in columns by the field that says what stage each one is at.
  • SplitView — Somebody reads records one after another and wants the one they picked open beside the list rather than on its own page.
  • StatTile — The card is ONE NUMBER rather than one record — a headline figure with its label and trend.

Each card is a real button, so it is in the tab order, is activated by Enter and Space, and draws a focus ring — no roles or key handling to add. The icon tile and the caret are hidden from assistive technology, and a name too long for its card is truncated with the full text kept as its title. Left to the author: the card’s accessible name is the title string, so a title that reads “—” or a bare number tells a screen-reader user nothing about which record they are opening.

The same three records the other three list bodies draw, as cards: the reference as the name, the account as the sub-line, and the value as a metric.

{
"id": "example",
"section": "The orders list",
"columns": 1,
"items": [
{
"id": "card_grid_1",
"type": "component",
"key": "card_grid",
"inputs": {
"items": [
{
"id": "o1",
"title": "ORD-4471",
"subtitle": "Meridian Supply · Draft",
"metrics": [
{
"value": "12,400",
"label": "total"
}
]
},
{
"id": "o2",
"title": "ORD-4472",
"subtitle": "Calder & Finch · Approved",
"metrics": [
{
"value": "3,150",
"label": "total"
}
]
},
{
"id": "o3",
"title": "ORD-4473",
"subtitle": "Brightwater Group · In review",
"metrics": [
{
"value": "86,900",
"label": "total"
}
]
}
]
}
}
]
}
<caos-card-grid id="cardGrid1"></caos-card-grid>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const cardGrid1 = document.getElementById('cardGrid1');
cardGrid1.items = [
{
"id": "o1",
"title": "ORD-4471",
"subtitle": "Meridian Supply · Draft",
"metrics": [
{
"value": "12,400",
"label": "total"
}
]
},
{
"id": "o2",
"title": "ORD-4472",
"subtitle": "Calder & Finch · Approved",
"metrics": [
{
"value": "3,150",
"label": "total"
}
]
},
{
"id": "o3",
"title": "ORD-4473",
"subtitle": "Brightwater Group · In review",
"metrics": [
{
"value": "86,900",
"label": "total"
}
]
}
];

The compact card doing what it was measured against: a segment sub-line and two rollup counts — which is a different card from the orders above, where one figure was the point.

{
"id": "example",
"section": "Accounts, with rollups",
"columns": 1,
"items": [
{
"id": "card_grid_1",
"type": "component",
"key": "card_grid",
"inputs": {
"items": [
{
"id": "a1",
"title": "Meridian Supply",
"subtitle": "Customer · Enterprise",
"metrics": [
{
"value": "3",
"label": "contacts"
},
{
"value": "2",
"label": "orders"
}
]
},
{
"id": "a2",
"title": "Calder & Finch",
"subtitle": "Partner · Mid-market",
"metrics": [
{
"value": "1",
"label": "contact"
},
{
"value": "5",
"label": "orders"
}
]
},
{
"id": "a3",
"title": "Brightwater Group",
"subtitle": "Prospect · Small business",
"metrics": [
{
"value": "1",
"label": "order"
}
]
}
]
}
}
]
}
<caos-card-grid id="cardGrid1"></caos-card-grid>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const cardGrid1 = document.getElementById('cardGrid1');
cardGrid1.items = [
{
"id": "a1",
"title": "Meridian Supply",
"subtitle": "Customer · Enterprise",
"metrics": [
{
"value": "3",
"label": "contacts"
},
{
"value": "2",
"label": "orders"
}
]
},
{
"id": "a2",
"title": "Calder & Finch",
"subtitle": "Partner · Mid-market",
"metrics": [
{
"value": "1",
"label": "contact"
},
{
"value": "5",
"label": "orders"
}
]
},
{
"id": "a3",
"title": "Brightwater Group",
"subtitle": "Prospect · Small business",
"metrics": [
{
"value": "1",
"label": "order"
}
]
}
];

An empty grid, which prints one plain muted sentence of its own rather than composing an EmptyState the way DataTable does — so a surface that wants to say something useful has to say it above the grid.

{
"id": "example",
"section": "Nothing to show",
"columns": 1,
"items": [
{
"id": "card_grid_1",
"type": "component",
"key": "card_grid",
"inputs": {
"items": []
}
}
]
}
<caos-card-grid id="cardGrid1"></caos-card-grid>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const cardGrid1 = document.getElementById('cardGrid1');
cardGrid1.items = [];