Skip to content

ComparisonTable

Generated from front/ui/catalogue/examples/ComparisonTable.json

section · static (no JavaScript) · no children. All components: component playground.

Feature comparison of products in columns, features in rows. Every competitor cell must come from a sourced data file (value, note, source URL, checked date); list the sources and the ‘as of’ date under the table. Neutral values only.

Rendered from the example props below with the real component. Theme: coffee · oncall · nightsky · gravity · poland · ulam · platform · open on its own

{
"component": "ComparisonTable",
"props": {
"id": "compare",
"title": "Brew methods",
"caption": "Comparison of three brew methods",
"asOf": "October 2026",
"columns": [
{
"label": "Pour-over",
"highlight": true
},
{
"label": "French press"
},
{
"label": "Espresso"
}
],
"rows": [
{
"label": "Needs a grinder",
"cells": [
{
"value": "Yes"
},
{
"value": "Yes"
},
{
"value": "Yes"
}
]
},
{
"label": "Brew time",
"cells": [
{
"value": "3 min"
},
{
"value": "4 min"
},
{
"value": "30 s"
}
]
},
{
"label": "Paper filter",
"cells": [
{
"value": "Yes"
},
{
"value": "No"
},
{
"value": "No"
}
]
}
]
}
}
Prop Type Required Notes
eyebrow string Small label above the title. max length 80
title string Section title. max length 160
intro string One or two sentences under the title. max length 600
caption string yes Table caption read by screen readers (what is compared). max length 160
asOf string When the facts were checked, e.g. ‘October 2026’. max length 40
columns array of objects Products, in column order (omit when groups is set). max 8 items
columns[].label string yes Product name, plain text (no logos). max length 40
columns[].note string Small line under the name, e.g. ‘hosted SaaS’. max length 40
columns[].highlight boolean The column of our own product. default false
rows array of objects Features (omit when groups is set). max 24 items
rows[].label string yes Feature. max length 60
rows[].help string What the row means. max length 160
rows[].cells array of objects yes One cell per column, same order. max 8 items
rows[].cells[].value string yes Yes, No, Partial, Via plugin, Paid add-on, Not documented, Coming, or a short phrase. max length 60
rows[].cells[].note string Short neutral note. max length 120
rows[].cells[].kind string Value kind of a translated cell: yes, no, partial, plugin, paid, unknown or coming. max length 16
groups array of objects Several tables behind a CSS-only segmented control (no JavaScript); each group lists its own products and features. Omit for a single table (columns and rows). max 4 items
groups[].label string yes Name of the group, shown on the segmented control. max length 40
groups[].caption string Table caption for this group. max length 160
groups[].columns array of objects yes Products, in column order. min 2 items, max 8 items
groups[].columns[].label string yes Product name, plain text (no logos). max length 40
groups[].columns[].note string Small line under the name, e.g. ‘hosted SaaS’. max length 40
groups[].columns[].highlight boolean The column of our own product. default false
groups[].rows array of objects Features (omit when sections is set). max 40 items
groups[].rows[].label string yes Feature. max length 60
groups[].rows[].help string What the row means. max length 160
groups[].rows[].cells array of objects yes One cell per column, same order. max 8 items
groups[].rows[].cells[].value string yes Yes, No, Partial, Via plugin, Paid add-on, Not documented, Coming, or a short phrase. max length 60
groups[].rows[].cells[].note string Short neutral note. max length 120
groups[].rows[].cells[].kind string Value kind of a translated cell: yes, no, partial, plugin, paid, unknown or coming. max length 16
groups[].sections array of objects Rows grouped under header rows (each section is its own tbody with a rowgroup header). max 8 items
groups[].sections[].label string yes Section heading shown as a header row, e.g. ‘Developer & headless’. max length 60
groups[].sections[].rows array of objects yes Features in this section. min 1 items, max 24 items
groups[].sections[].rows[].label string yes Feature. max length 60
groups[].sections[].rows[].help string What the row means. max length 160
groups[].sections[].rows[].cells array of objects yes One cell per column, same order. max 8 items
groups[].sections[].rows[].cells[].value string yes Yes, No, Partial, Via plugin, Paid add-on, Not documented, Coming, or a short phrase. max length 60
groups[].sections[].rows[].cells[].note string Short neutral note. max length 120
groups[].sections[].rows[].cells[].kind string Value kind of a translated cell: yes, no, partial, plugin, paid, unknown or coming. max length 16
sources array of objects Sources for the cells. max 400 items
sources[].label string yes What the source supports, e.g. ‘Moodle: SCORM, H5P’. max length 300
sources[].href string (href) yes Source URL. max length 2048
sources[].checked string Date checked. max length 20
note string Small print under the table. max length 300
id string Anchor id for in-page links (letters, digits, dashes). max length 40

What a text-only client, an unknown renderer or a plain-text channel shows for the example above.

Brew methods
Comparison of three brew methods

A node whose props fail the schema never breaks the page: the renderer shows the plain-text fallback instead (and logs the issues in development).

{
"rows": "many"
}

Issues:

  • /caption is required
  • /rows expected array, got string

Rendered as:

Nothing: this component has no text of its own.

axe-core: 24 rules passed, 0 violations, 0 need manual review. Run at build time on the server-rendered HTML in jsdom. Colour contrast and layout rules need a browser and are covered by the Playwright axe scans of the reference frontend.