ComparisonTable
Features down the side and options across, with ticks, crosses and values in the cells, and one option picked out.
Demo
| Feature | Free | RecommendedTeam | Business |
|---|---|---|---|
| Projects | 3 | Unlimited | Unlimited |
| MembersGuests are free | 1 | Up to 50 | Unlimited |
| Version history | No | Yes | Yes |
| Single sign-on | No | No | Yes |
| Support | |||
| EmailWithin a day | Yes | Yes | Yes |
| Phone | No | Yes | |
| Feature | Vim | Emacs | VS Code |
|---|---|---|---|
| Modal editing | Yes | No | No |
| Written in | C | Lisp | TypeScript |
| First release | 1991 | 1976 | 2015 |
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/comparison_table.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/comparison_table.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/comparison_table.qwe
view ComparisonTableDemo() = <div.demo-stack> <ComparisonTable id="comparison-table-demo-plans" label="Plans compared" picked={1} options={[{ label: "Free", meta: "$0" }, { label: "Team", meta: "$12 / user / month" }, { label: "Business", meta: "Talk to us" }]} features={[ { label: "Projects", values: ["3", "Unlimited", "Unlimited"] }, { label: "Members", values: ["1", "Up to 50", "Unlimited"], hint: "Guests are free" }, { label: "Version history", values: [false, true, true] }, { label: "Single sign-on", values: [false, false, true] }, { group: "Support" }, { label: "Email", values: [true, true, true], hint: "Within a day" }, { label: "Phone", values: [null, false, true] }, ]}/> <ComparisonTable id="comparison-table-demo-tools" label="Editors compared" options={["Vim", "Emacs", "VS Code"]} features={[ { label: "Modal editing", values: [true, false, false] }, { label: "Written in", values: ["C", "Lisp", "TypeScript"] }, { label: "First release", values: ["1991", "1976", "2015"] }, ]}/> </div>Source
ui/comparison_table.qwe
// ComparisonTable: features down the side, options across the top, and in each// cell a tick, a cross or a value: plans, products or tools side by side.//// <ComparisonTable label="Plans" options={["Free", "Team", "Business"]} picked={1}// features={[// { label: "Projects", values: ["3", "Unlimited", "Unlimited"] },// { label: "Single sign-on", values: [false, false, true] },// { group: "Support" },// { label: "Email", values: [true, true, true], hint: "Within a day" },// ]}/>//// options: the names across the top, strings or maps of { label, meta? } (meta: a// line under the name, "$12 / month"). features: rows of { label, values, hint? },// values one per option: true is a tick, false a cross, null a dash, anything else// its text; hint is a line under the feature's name. A row of only { group } is a// heading between features. picked: the index of the option picked out (its// column shaded and named with picked_label, "Recommended"). label: the table's// name for screen readers. id: the element's id. The first column stays in view// while it scrolls sideways on a narrow screen.import ui/theme.qwe use ui_clslet comparison_table_css = css`.ui-comparison { min-width: 0; overflow: auto; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-surface); font: 14px/1.45 var(--ui-font); color: var(--ui-fg); }.ui-comparison table { width: 100%; border-collapse: separate; border-spacing: 0; }.ui-comparison th, .ui-comparison td { padding: 10px 14px; border-bottom: 1px solid var(--ui-border); vertical-align: top; }.ui-comparison td { text-align: center; }.ui-comparison thead th { text-align: center; background: var(--ui-subtle); font-weight: 600; }.ui-comparison thead th:first-child, .ui-comparison tbody th { position: sticky; left: 0; z-index: 1; text-align: left; font-weight: 500; background: var(--ui-surface); }.ui-comparison thead th:first-child { background: var(--ui-subtle); }.ui-comparison tbody tr:last-child th, .ui-comparison tbody tr:last-child td { border-bottom: 0; }.ui-comparison-meta, .ui-comparison-hint { display: block; margin-top: 2px; color: var(--ui-muted); font-size: 12px; font-weight: 400; }.ui-comparison-group th { padding-top: 16px; background: var(--ui-surface); color: var(--ui-muted); font-size: 12px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; }.ui-comparison-picked { background: color-mix(in srgb, var(--ui-accent-soft) 55%, var(--ui-surface)); }.ui-comparison thead th.ui-comparison-picked { background: var(--ui-accent-soft); color: var(--ui-accent); }.ui-comparison-tagline { display: block; margin-bottom: 4px; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }.ui-comparison-yes, .ui-comparison-no, .ui-comparison-none { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; vertical-align: middle; }.ui-comparison-yes { background: var(--ui-success-soft); color: var(--ui-success); }.ui-comparison-no { background: var(--ui-subtle); color: var(--ui-muted); }.ui-comparison-none { color: var(--ui-muted); }.ui-comparison-yes svg, .ui-comparison-no svg { width: 12px; height: 12px; }`// An option as { label, meta }.fn comparison_table_option(o) -> map = type_of(o) == "map" ? o : { label: str(o) }view ComparisonTable(options: list, features: list, picked: int? = null, picked_label: str = "Recommended", label: str? = null, id: str? = null, class: str? = null) { style global comparison_table_css let opts = options | map((o) => comparison_table_option(o)) <div class={ui_cls(["ui-comparison", class])} id={id}> <table aria-label={label}> <thead> <tr> <th scope="col"><span.ui-sr>Feature</span></th> {for [i, o] in enumerate(opts) => <th scope="col" class={i == picked ? "ui-comparison-picked" : null}> {i == picked && <span.ui-comparison-tagline>{picked_label}</span>} {o.label} {o.meta != null && <span.ui-comparison-meta>{o.meta}</span>} </th>} </tr> </thead> <tbody> {for f in features => f.group != null ? <tr.ui-comparison-group><th scope="colgroup" colspan={str(len(opts) + 1)}>{f.group}</th></tr> : <tr> <th scope="row">{f.label}{f.hint != null && <span.ui-comparison-hint>{f.hint}</span>}</th> {for [i, v] in enumerate(opts) => { let val = (f.values ?? [])[i] <td class={i == picked ? "ui-comparison-picked" : null}> {val == true ? <span.ui-comparison-yes><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg><span.ui-sr>Yes</span></span> : val == false ? <span.ui-comparison-no><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg><span.ui-sr>No</span></span> : val == null ? <span.ui-comparison-none aria-hidden="true">—</span> : str(val)} </td> }} </tr>} </tbody> </table> </div>}Related
AuditLogWho did what to what and when, newest first, filtered by person and action, searched, and paged.5 views · 7 downloadsBarcodeA Code 128 barcode drawn as SVG from text, switching to the compact digit set for long numbers, with the text under it.4 views · 9 downloadsCsvTableA table made from CSV text, with quoted fields, a header row, numbers lined up on the right and a row limit.6 views · 7 downloadsDataGridSort, search, filter by a column's values, pick rows and page through them, in one table.5 views · 8 downloadsDataTableRows of data sorted by their headers, with search, pages, number, money, percent and date columns.5 views · 9 downloadsEditableTableA grid edited in place: text, number, select, checkbox and date cells, checked, sorted, with rows added and deleted.7 views · 5 downloads
Thread
Thread 0 posts
On ComparisonTable: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.