Skip to main content
View as Markdown

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.

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.
app.yaml
- 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.
app.yaml
- 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.
app.yaml
- 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.
app.yaml
- type: data-timeline
  dataSource: { table: phases }
  props:
    startField: starts_on
    endField: ends_on
    labelField: name
    groupBy: workstream
    defaultZoom: month

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:

app.yaml
tables:
  - name: tasks
    fields:
      - name: priority
        type: single-select
        options:
          - { value: urgent, color: '#DC2626' }
          - { value: normal, color: '#2563EB' }
app.yaml
- type: kanban
  dataSource: { table: tasks }
  kanbanGroupBy: { field: status }
  card:
    colorField: priority # urgent cards fill #DC2626, normal cards #2563EB

Text 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.

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.

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.

Built with Sovrium