Skip to content

SplitView

<caos-split-view>

the SPLIT body of a list surface — the records on the left, the one you are reading on the right

May be placed on a page inside an app.

Name Attribute Description Type Default Required
items property only The records. Each one an id, the title, the compact secondary fields the left lane shows, and the full detail field set the right pane reads — every value already formatted and every reference already resolved, with an href on a field that should be a link. Set as a property; an attribute cannot hold a list. Handing in a new list keeps the current selection if it is still there, and otherwise falls to the first record. json none declared Optional
Name When it fires What it carries
caos-split-open The “Open as tab” control in the detail header is pressed, or — on a narrow viewport, where there is no detail pane — a record in the lane is tapped. { id } — the record to open. The host routes it; the split view does not navigate.
caos-navigate A link inside a detail field is clicked. The default navigation is prevented first. { href } — the route the field pointed at.

Nothing goes inside this piece.

Design token What it controls
--caos-font The typeface of both panes.
--caos-color-border The rule above the whole piece, and a lane card at rest.
--caos-color-border-strong The divider between the lane and the detail pane.
--caos-color-surface The ground of a lane card.
--caos-color-accent The record names, the links, and the selected card border and rail.
--caos-accent-wash The tint filling the selected lane card.
--caos-focus-ring The ring on a focused lane card or link.
--caos-radius-sm The corners of that ring on a link.
--caos-color-text-muted The lane field lines, the detail field labels, and “Open as tab”.
--caos-font-display The face of the detail title.
--caos-font-mono The detail field labels and the “Open as tab” control.
--caos-color-text The detail field values.
--caos-radius-sm The corners of a lane card.
--caos-space-2 The padding of the lane, the gap between its cards, and the gap between a card’s name and its fields.
--caos-space-3 The padding inside a lane card, the gap between its field lines, and the gap between the detail title and “Open as tab”.
--caos-space-1 The rows between a card’s field lines, the gap inside “Open as tab”, and the gap above a detail value.
--caos-space-4 The gap between detail fields, and the least horizontal padding of the detail pane.
--caos-space-5 The top padding of the detail pane, the space above its fields, and the padding of the empty state.
--caos-space-6 The most horizontal padding of the detail pane on a wide screen.

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(wrap) the two-pane wrapper Always
::part(lane) the list lane Always
::part(detail) the record pane beside it Always

A list somebody works DOWN — triaging, reviewing, checking one record after another — where opening each one properly and coming back would cost more than reading it is worth.

  • DataTable — The records are being compared field by field, or need selecting, sorting or totalling — and each one is read on its own page when it comes to that.
  • CardGrid — The list is browsed rather than worked through, and a name with a couple of rollups is enough.
  • KanbanBoard — The question is which stage the records are at rather than what any one of them says.

Every lane record is a real button, in the tab order and activated by Enter or Space, and the selected one carries aria-current so it is not identified by its tint alone. The links inside detail fields are real anchors with a real href, so they read and behave as links even though the host does the routing. Left to the author: the piece has no landmark or accessible name of its own, so the surface must say what list this is; and selecting a record in the lane rewrites the detail pane without announcing it, so a screen-reader user is told nothing changed.

The same three records the other three list bodies draw, as a lane beside the one being read: two compact fields per lane card, the full field set in the pane, and the account resolved as a link that reports where it wants to go.

{
"id": "example",
"section": "The orders list",
"columns": 1,
"items": [
{
"id": "split_view_1",
"type": "component",
"key": "split_view",
"inputs": {
"items": [
{
"id": "o1",
"title": "ORD-4471",
"secondary": [
{
"label": "Account",
"value": "Meridian Supply"
},
{
"label": "Status",
"value": "Draft"
}
],
"detail": [
{
"label": "Account",
"value": "Meridian Supply",
"href": "/sales-atlas/account/a1"
},
{
"label": "Status",
"value": "Draft"
},
{
"label": "Total",
"value": "12,400"
},
{
"label": "Owner",
"value": "Dana Whitfield"
}
]
},
{
"id": "o2",
"title": "ORD-4472",
"secondary": [
{
"label": "Account",
"value": "Calder & Finch"
},
{
"label": "Status",
"value": "Approved"
}
],
"detail": [
{
"label": "Account",
"value": "Calder & Finch",
"href": "/sales-atlas/account/a2"
},
{
"label": "Status",
"value": "Approved"
},
{
"label": "Total",
"value": "3,150"
},
{
"label": "Owner",
"value": "Priya Raman"
}
]
},
{
"id": "o3",
"title": "ORD-4473",
"secondary": [
{
"label": "Account",
"value": "Brightwater Group"
},
{
"label": "Status",
"value": "In review"
}
],
"detail": [
{
"label": "Account",
"value": "Brightwater Group",
"href": "/sales-atlas/account/a3"
},
{
"label": "Status",
"value": "In review"
},
{
"label": "Total",
"value": "86,900"
},
{
"label": "Owner",
"value": "Dana Whitfield"
}
]
}
]
}
}
]
}
<caos-split-view id="splitView1"></caos-split-view>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const splitView1 = document.getElementById('splitView1');
splitView1.items = [
{
"id": "o1",
"title": "ORD-4471",
"secondary": [
{
"label": "Account",
"value": "Meridian Supply"
},
{
"label": "Status",
"value": "Draft"
}
],
"detail": [
{
"label": "Account",
"value": "Meridian Supply",
"href": "/sales-atlas/account/a1"
},
{
"label": "Status",
"value": "Draft"
},
{
"label": "Total",
"value": "12,400"
},
{
"label": "Owner",
"value": "Dana Whitfield"
}
]
},
{
"id": "o2",
"title": "ORD-4472",
"secondary": [
{
"label": "Account",
"value": "Calder & Finch"
},
{
"label": "Status",
"value": "Approved"
}
],
"detail": [
{
"label": "Account",
"value": "Calder & Finch",
"href": "/sales-atlas/account/a2"
},
{
"label": "Status",
"value": "Approved"
},
{
"label": "Total",
"value": "3,150"
},
{
"label": "Owner",
"value": "Priya Raman"
}
]
},
{
"id": "o3",
"title": "ORD-4473",
"secondary": [
{
"label": "Account",
"value": "Brightwater Group"
},
{
"label": "Status",
"value": "In review"
}
],
"detail": [
{
"label": "Account",
"value": "Brightwater Group",
"href": "/sales-atlas/account/a3"
},
{
"label": "Status",
"value": "In review"
},
{
"label": "Total",
"value": "86,900"
},
{
"label": "Owner",
"value": "Dana Whitfield"
}
]
}
];

An empty lane, which prints one plain muted sentence and leaves the detail pane blank — so a surface that needs to explain why the list is empty has to say it above the split.

{
"id": "example",
"section": "Nothing in the list",
"columns": 1,
"items": [
{
"id": "split_view_1",
"type": "component",
"key": "split_view",
"inputs": {
"items": []
}
}
]
}
<caos-split-view id="splitView1"></caos-split-view>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const splitView1 = document.getElementById('splitView1');
splitView1.items = [];