Boards, Calendars & Timelines
Four components take the same records a table would show and arrange them by something other than row order — by card, by column, by date, by span. All four bind through the shared dataSource module.
gallery
A responsive card grid.
| Property | Description |
|---|---|
dataSource |
Table binding. |
gridColumns |
Column count per breakpoint — { mobile, sm, md, lg, xl }, each 1 to 6. |
galleryCard |
Card configuration (see below). |
layout |
grid (uniform rows) or masonry (variable heights). |
emptyMessage |
Shown when nothing matches. |
galleryCard property |
Description |
|---|---|
coverImage |
Cover image URL, usually a $record.* reference. |
aspectRatio |
Cover ratio, e.g. 4:3, 16:9, 1:1. |
children |
Components rendered in the card body. |
hoverOverlay |
{ children } rendered over the card on hover. |
onClick |
Action invoked when the card is clicked. |
- type: gallery
dataSource: { table: products }
gridColumns: { mobile: 1, md: 2, lg: 3 }
layout: masonry
galleryCard:
coverImage: '$record.photo'
aspectRatio: '4:3'
children:
- { type: text, element: h3, content: '$record.name' }kanban
A board grouping records into columns by a field value, with drag-and-drop between them.
| Property | Description |
|---|---|
dataSource |
Table binding. |
kanbanGroupBy |
{ field } — the field whose values become the columns. |
card |
Card configuration (see below). |
drag |
{ enabled, persistAction } — whether cards move, and what saves the move. persistAction must be a crud action ({ type: crud, operation: update, table }); it is the only type a drop runs. |
colorField |
No effect — accepted, then ignored. The board reads card.colorField. |
emptyColumnMessage |
Shown in a column with no cards. |
card property |
Description |
|---|---|
children |
Components rendered in the card body. |
coverImage |
Card cover image URL. |
colorField |
Field whose declared option colours fill the card. See Colouring records by a field. |
footer |
{ field, format } entries in the card footer. format is relative-date, short-date, avatar, badge, or text. |
onClick |
Action invoked when the card is clicked. |
- type: kanban
dataSource: { table: tasks }
kanbanGroupBy: { field: status }
drag: { enabled: true }
card:
children: [{ type: text, content: '$record.title' }]
footer: [{ field: assignee, format: avatar }, { field: due_date, format: short-date }]calendar
A month, week or day calendar of date-bearing records. The field mappings sit at the top level — only click handling lives under calendarEvent and calendarInteraction.
| Property | Description |
|---|---|
dataSource |
Table binding. |
dateField |
Field supplying each event's start. |
endDateField |
Field supplying its end, for events that span time. |
labelField |
Field rendered as the event's label. |
colorField |
Field colouring the event. A coloured event renders as a block — see below. |
defaultView |
month, week, or day. |
maxEventsPerDay |
Cap before a day cell collapses into a "+N more" affordance. |
calendarEvent.onEventClick |
Action invoked when an event is clicked. |
calendarInteraction.onDateClick |
Action invoked when an empty date or slot is clicked. |
calendarInteraction.timeSlotInterval |
Slot length in minutes for week and day views. |
calendarInteraction.showCurrentTimeIndicator |
Draw a line at the current time. |
- type: calendar
dataSource: { table: bookings }
dateField: starts_at
endDateField: ends_at
labelField: customer_name
defaultView: week
calendarInteraction: { timeSlotInterval: 30, showCurrentTimeIndicator: true }data-timeline
A Gantt-style view of records that occupy a span rather than an instant. Its schema is deliberately minimal — dataSource plus props — and the field mappings are read from props:
props key |
Description |
|---|---|
startField |
Required at render time. Field supplying each bar's start. |
endField |
Field supplying its end. |
labelField |
Field rendered as the bar's label. |
groupBy |
Field whose values become the timeline's rows. |
colorField |
Field whose value sets the bar color. |
defaultZoom |
day, week, month, quarter, or year. |
- type: data-timeline
dataSource: { table: phases }
props:
startField: starts_on
endField: ends_on
labelField: name
groupBy: workstream
defaultZoom: monthThese props keys are not schema-validated. A misspelled startFeild passes validation and the component renders its error state instead of a timeline. Check the spelling here more carefully than elsewhere.
Note that data-timeline is the data-bound Gantt island; the plain timeline in Display Components is a static structural list with a rail, and takes no data source.
Colouring records by a field
kanban, calendar and data-timeline each accept a colorField naming the field whose value colours the card, event or bar. The three spell it differently, because the key sits wherever each component already keeps its display config:
| Component | Key |
|---|---|
kanban |
card.colorField |
calendar |
colorField |
data-timeline |
props.colorField |
The colour comes from the option colours declared on that field. Only single-select, multi-select and status carry option declarations, so only those three can supply one:
tables:
- name: tasks
fields:
- name: priority
type: single-select
options:
- { value: urgent, color: '#DC2626' }
- { value: normal, color: '#2563EB' }- type: kanban
dataSource: { table: tasks }
kanbanGroupBy: { field: status }
card:
colorField: priority # urgent cards fill #DC2626, normal cards #2563EBText is not left to chance: each surface derives a foreground from the fill — whichever of black or white contrasts more — so a label stays legible against any hue an author picks.
A multi-select resolves only while a record holds exactly one option. A cell holding two or more matches no single declaration and takes the no-colour path below.
Declared colours now win, so existing boards change appearance. colorField used to ignore what the field declared and assign a colour from a built-in palette instead. It now reads the declarations. An app that already points colorField at a field with declared option colours renders those colours after upgrading, in place of the palette hues it showed before.
When a value declares no colour
The fallback differs per surface, and the difference is deliberate rather than an oversight:
| Surface | A value with no declared colour |
|---|---|
calendar, data-timeline |
Takes a hue from a built-in palette. Both surfaces already painted a hue per value, so they keep doing so. |
kanban |
Takes nothing — the card stays monochrome. A kanban card has never invented a hue, and colour stays something you opt into. |
Where a palette hue is used, it is now derived from the value itself, so a given value always draws the same hue. A timeline previously assigned palette hues in order of first appearance, which meant a bar changed colour as you paged or filtered the records around it.
The fallback applies per value, not per field: a field that declares colours on some options and leaves others bare sends only the bare ones down this path.
Calendar: a coloured event is a block, not a dot
In month view a calendar renders a timed event as a small dot by default. A dot is mostly empty space, so a colour applied to it is close to invisible. An event that carries a colour therefore renders as a filled block instead; an event with no colour keeps the default dot.
Expect this as a visible layout change on any month-view calendar that sets colorField — day cells that used to hold lines of dots now hold bands of filled blocks.
colorField is not validated. A misspelled field name, or a field type that has no options, is not an error. The surface simply takes its no-colour path: a kanban board stays monochrome; a calendar or timeline falls back to palette hues keyed on the raw value. Nothing reports the typo. The grid's rowColorField is the exception — sovrium validate checks that one.
Related Pages
- Tables & Lists —
data-table,list,data-form. - Charts & KPIs — aggregated visualizations.
- Data Binding —
dataSource, filters, pagination. - Interactions & Auto-Save — the actions these components invoke.
- Display Components — the non-data
timeline.
Last updated September 1, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.