RoadieRoadie
  • Home
  • Brand
  • Foundations
  • Content
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeBrandFoundations
Appearance

Grouped tables, totals, and stacked areas

Changelog·24 Oct 2026

Insight pages can group and total their tables, chart part of a whole over time, compare events by days out or on the event clock, and set a dashboard's period by dragging across a chart.

  • Components 2.17.0
  • Core 2.12.0
  • Charts 0.3.0
  • Widgets 3.7.1
  • Lint plugin 0.2.0

Highlights

  • StackedArea

    A chart for part of a whole over time, such as scans by gate, with a share mode, a today marker, and annotations.

  • Grouped tables

    DataTable groups rows by one or more columns, with a total for each group, and RecordTable gains a totals row.

    8,412

  • Range selection

    Drag across a line, bar, or stacked area chart to select dates, or set a dashboard's period from a card.

  • Event comparisons

    Line charts line events up by days out or on the event's own clock, where a night runs to 6am.

  • @oztix/roadie-lint-plugin

    Breaking

    The ESLint plugin is now @oztix/roadie-lint-plugin and runs in oxlint too, with a preset that catches tests that can't fail.

New

15
  • Add size (sm, md, lg) to Checkbox, CheckboxGroup, and RadioGroup, and trailing to Checkbox and RadioGroup.Item for a count or value at the end of the row.

    The default md box is now 20px, down from 24px, which also makes RecordTable's and Records' selection checkboxes smaller; pass size='lg' to keep the old size. A ticked or indeterminate checkbox and a chosen radio now fill with the accent's strong colour, with the icon or dot in the text colour for strong, and drop the sunken inset of an empty box. Stacked items in a CheckboxGroup or RadioGroup sit 2px apart at sm and 4px at md, down from 8px, and stay 8px apart at lg. A radio's dot is now half its circle at every size.

    SeeCheckbox·Radio group

  • DataCard and Chart take controls: switches for what the card's body shows, such as a chart's Cumulative, Incremental and Percentage modes.

    They sit in a full-width row under the headline, wrap on a narrow card, and stay in place in every state and in a chart's Table view. StatTile doesn't take them.

    SeeDataCard·Chart

  • Add groupBy and defaultExpanded to DataTable.

    Rows group by one or more text or status columns, outermost first. Each group gets a row that sums its number columns the way totals does, which opens and closes like an Accordion when anywhere on it is pressed, and every level's label sits indented in the title column, or the first column without a sum when there's no text column. Groups start closed: defaultExpanded opens them all, or the groups at the paths of values you give. Sorting orders groups by their sums, or by label, at every level. Dashboard table cards take the same two fields, DashboardView passes them on, and validateDashboard checks them.

    SeeDataTable

  • Add totals to RecordTable and useRecords: a row of totals under the headings, over every record the search and filters match.

    totals sums the number and money fields in the browser; with server data, pass the server's sums as { values }, or resolve them from loadRange beside rowCount. It labels itself "Totals for 1,234 shows" from recordName, and reads as one line above the rows on a phone. A column's total: false leaves it blank. Tables without totals don't change.

    SeeRecordTable

  • Show the total of a number column that comes first in DataTable.

    The totals label, each group's label, and the grouped header now go in the title column, the pinned text column or else the first text column, as in RecordTable. A table with no text column puts them in the first column without a sum, so the label never covers a total. validateDashboard follows the same rule: it warns about a totals row only when every column has a sum, or when values gives one for the label's column, and about grouped rows only when there's no text column and the label's column isn't status. In core, tableLabelIndex(columns) finds the label's column, tableTitleColumn(columns) the title, and labelColumnIndex applies the rule to columns of another shape; totalsLabel(count, recordName, exact) writes "Totals for 1,234 shows", with a + after a count that's a floor.

    SeeDataTable

  • Add selectsPeriod to the line, bar, and stacked-area plots of a dashboard description, so a drag across that card, or Shift with an arrow key and then Enter, sets the dashboard's period.

    DashboardView passes the range to onPeriodChange with the current comparison, and validateDashboard warns when x isn't dated or there's no period. Dashboards without it don't change.

    SeeDashboard design·Dashboard

  • Add xFormat to LineChart and the dashboard line plot, with a daysOut format for comparing events by days to the event.

    Ticks read "30d", and tooltips, tables and screen readers say "30 days before" and "Event day". Core adds XFormat and formatX to @oztix/roadie-core/dataviz.

    SeeDashboard

  • Add an eventTime x format to LineChart and the dashboard line plot, for comparing events on their own clock.

    x is hours from midnight starting the first event day, and a day runs to 6am. Ticks read "Day 2" and "19:00", and tooltips, tables and screen readers say "Day 2, 7:15pm". Core exports END_OF_NIGHT_HOUR from @oztix/roadie-core/datetime, the 6am cutoff that both a late show's night and an eventTime day share.

  • Add toElasticsearch on @oztix/roadie-core/records/elasticsearch, which turns a records request into Elasticsearch query DSL (query, sort, from, and size) that means the same as matchesRecordQuery.

    paths maps a field to the path filters and sort read, such as a keyword sub-field, and searchFields sets what search reads. except leaves out one field's chips, so a page can count that field's values against every other filter. It needs Elasticsearch 7.10 or later, or OpenSearch.

    SeeRecords model

  • Add mode, today and annotations to StackedArea and the dashboard stacked-area plot.

    mode='share' stacks every time to 100%, today marks the top of the stack with a dot that reads the total, and annotations work as on LineChart.

  • Add StackedArea (@oztix/roadie-charts/stacked-area), a chart for part of a whole over time, such as scans by gate.

    The largest layer stacks at the bottom, past 6 the smallest roll into Other on top, and the tooltip opens with the total. Dashboards take it as plot kind stacked-area, with /static and /tables support. ChartTooltip rows take shape: 'none' for a row with no key, such as a total.

    SeeStackedArea

  • Add onRangeSelect to LineChart, BarChart, and StackedArea, so a drag across dated x, or Shift with an arrow key and then Enter, selects a range.

    It reports an inclusive AbsoluteRange in the venue's wall time, such as { start: '2026-10-03', end: '2026-10-10' }, which fits straight into DateRangePicker or onPeriodChange. Charts without it don't change.

    SeeLineChart·BarChart·StackedArea

  • Add canonical-class, which fails a Tailwind class not in its canonical form, such as p-[16px], and fixes it to its canonical form, such as p-4.

    It reads Roadie's theme through tailwindcss, a new optional peer dependency at 4.1.15 or later. The recommended preset turns it on as an error in ESLint and oxlint, so an app can report new errors after upgrading: run the linter with --fix to rewrite them.

  • Add the testing preset, which catches tests that can't be trusted: roadie/no-css-class-in-jsdom fails a calc(), container query, or breakpoint class in toHaveClass, roadie/no-css-source-in-jsdom fails a test that reads CSS source, roadie/no-layout-fake-in-jsdom fails a faked layout read, such as getBoundingClientRect, or a faked viewport media query, roadie/no-cva-output-assertion fails an assertion on the class string a variants function returns, and roadie/no-fixed-sleep fails a fixed wait, such as setTimeout inside a promise or page.waitForTimeout.

    In oxlint, extend oxlint/testing.json. In ESLint, spread configs.testing into your config array. The rules apply to *.test.* files. Tests that run in a real browser skip the jsdom rules: *.browser.test.* files, *.spec.* files, which Playwright runs, and files in an e2e folder. Those keep roadie/no-fixed-sleep, and a *.test.* file among them keeps roadie/no-cva-output-assertion too. The linting guide shows both setups.

  • Add meta.docs.url to each rule, linking it to its section of the linting guide, so an editor can open the guide from a reported rule ID.

Changed

4
  • Update chart time ticks, tooltips, tables and spoken points to the date and time foundations.

    Ticks read "19:30" and "27 Nov", and tooltips read "Fri 27 Nov 2026, 7:30pm", the same strings <DateTime> gives.

    SeeDateTime

  • Update CartContents to stable, so a breaking change to it only comes in a major version.

    SeeCartContents

  • Breaking: @oztix/roadie-lint-plugin is the new name of @oztix/roadie-eslint-plugin, and it now runs in oxlint as well as ESLint. Breaking

    To upgrade, swap the package in package.json and change your config's import to the new name. The rules, their roadie/ IDs, and configs.recommended are the same.

    For oxlint 1.88 or later, extend ./node_modules/@oztix/roadie-lint-plugin/oxlint/recommended.json from .oxlintrc.json, or import @oztix/roadie-lint-plugin/oxlint/recommended.json in oxlint.config.ts. The preset loads the plugin as roadie, so rule IDs match ESLint's. eslint and oxlint are both optional peer dependencies, so an app that uses one doesn't get a missing-peer warning for the other. The linting guide shows both setups.

  • Update the README to lead with oxlint.

    ESLint 9 and 10 still run the same rules, for an app that can't move to oxlint yet.

Fixed

14
  • Fix an open Accordion item growing past its container when it holds wide content, such as a code block, so it no longer pushes a phone page sideways.

    SeeAccordion

  • RecordTable and RecordGrid in a fill box centre their empty and error states in the space under the header, instead of showing them at the top.

    Rows, cards, and loading placeholders still start at the top.

  • Fix a ticked or indeterminate Checkbox that's invalid, which now fills with danger instead of the accent, so the error still shows.

    SeeCheckbox

  • Fix pressing a later pinned DataTable cell in a linked or grouped row, which now follows the row's link or toggles its group like any other cell.

    A number column that groupBy leaves first also keeps its figures end-aligned, and a linked row's title is a subtle Link, underlined in the accent colour while the row is hovered or focused.

    SeeDataTable

  • A List row with long text no longer widens the list past its container on a narrow screen; the title truncates and the trailing slot stays in view.

    SeeList

  • Fix an is-interactive-within surface taking the hover, press and focus ring of a nested surface's target.

    A Card around a grouped DataTable, linked DataTable rows or RecordTable list rows now stays still when one of those rows is hovered, pressed or focused, and a subtler card keeps its tint. A target now belongs to the nearest is-interactive-within around it.

    SeeCard·DataTable·RecordTable

  • Fix the rows in a QueryField or Records.Search list jumping as the highlight moves.

    The row with the Enter hint was 1px taller than the rest, so every row below it shifted. Each row is now tall enough for the hint.

    SeeQuery field·Records model

  • RadioGroup now dims a disabled group or item and ignores presses on its row, like Checkbox.

    An invalid group, from invalid or its Field, draws its empty circles with a danger border and tint, fills the chosen one with danger like an invalid ticked Checkbox, and sets aria-invalid on the radiogroup.

    SeeRadio group·Checkbox

  • RecordTable puts the totals label in the title column, or with no title in the first column without a sum that isn't an image, as DataTable does, so the label no longer covers a summed column's total, and in the title column it no longer hides with a column's priority.

    With totals and records selected, the bulk actions bar's focus rings are no longer clipped at the top.

    SeeDataTable

  • Fix a scrolling ToggleGroup jumping back to its pressed item when it resized while keyboard focus was on another item, which hid the focused item.

    It now keeps the focused item in view, and the pressed one when focus is elsewhere.

    SeeToggle group

  • BarChart with onRangeSelect now shades the selected bars whole, from the first bar's left edge to the last bar's right edge, instead of from centre to centre.

    SeeBarChart

  • Clicking a chart no longer draws a focus ring around it in Chrome.

    Tabbing to a chart still shows the focus ring.

  • LineChart's today dot now sits on the highlighted series, as the forecast cone does, instead of always on the first series.

    A numeric x axis no longer borrows the y format, so days stop rendering as percentages.

  • LineChart and StackedArea keep an annotation label clear of the today label.

    A label that would cross it reads on the other side of its rule, or drops a line when neither side is clear.

  • Newer

    Updates
  • Older

    Editor, records, and date pickers