Patterns

Table card

The canonical Yuno table-block chrome — sibling of ChartCard. A header (title + optional info icon on the left, a tooltip or the definition of what the table counts; an actions cluster on the right with an optional slot, a View all link, and an Export button) over a bordered table passed as children. Mined from the Reconciliations screen, where it repeats across every provider table.

Updated Oct 2, 2026 by Juan Pablo Turina

Anatomy

A header row (title + optional (i) info icon on the left; actions on the right) sits 24px above the table. The table is the kit Table — which already ships its own 1px bordered, rounded container, so it is never wrapped again. A TableFooter carries the bold Total row.

Not reconciled transactions by provider

ProviderTotal transactionsNot reconciled amount USD
dLocal1.234.981$68.190.346 USD
Mercado Pago1.234.981$68.190.346 USD
Adyen1.234.981$68.190.346 USD
Total8.800.000$5.689.190.346 USD
  1. 1
    Title + info

    The block title. section variant = 24px semibold; card variant = 16px semibold. An optional (i) icon via the info prop: a string reveals a Tooltip on hover, a ChartDefinition opens a Popover on click that says what the table counts, and with showPer prints the unit as a secondary Badge after the icon.

  2. 2
    View all

    A blue link (Button variant=link) that appears when onViewAll is set.

  3. 3
    Export

    An outline Button with a DownloadSimple icon, shown when onExport is set. An actions slot sits before it for extra controls (e.g. a By date dropdown).

  4. 4
    Table

    The kit Table passed as children — provider cells (logo + name), amounts (semibold + muted USD), a row-action kebab, and a bold Total in the TableFooter.

States

Section variant

variant="section" (default): a big 24px title with actions, then the bordered table. No outer card. Use for a full-width section block like Not reconciled or Reconciled by type.

Not reconciled transactions by provider

ProviderTotal transactionsNot reconciled amount USD
dLocal1.234.981$68.190.346 USD
Mercado Pago1.234.981$68.190.346 USD
Adyen1.234.981$68.190.346 USD
Total8.800.000$5.689.190.346 USD
<TableCard
  title="Not reconciled transactions by provider"
  info="Transactions that could not be matched…"
  onViewAll={openAll}
  onExport={exportCsv}
>
  <Table>
    <TableHeader>…</TableHeader>
    <TableBody>…</TableBody>
    <TableFooter>{/* bold Total row */}</TableFooter>
  </Table>
</TableCard>
Card variant

variant="card": a 16px title inside a padded Card (24px). Use for side-by-side blocks like Status conflict / Amount conflict.

Status conflict

ProviderTotal transactionsNot reconciled amount USD
dLocal1.234.981$68.190.346 USD
Mercado Pago1.234.981$68.190.346 USD
Adyen1.234.981$68.190.346 USD
Total8.800.000$5.689.190.346 USD

Amount conflict

ProviderTotal transactionsNot reconciled amount USD
dLocal1.234.981$68.190.346 USD
Mercado Pago1.234.981$68.190.346 USD
Adyen1.234.981$68.190.346 USD
Total8.800.000$5.689.190.346 USD
<TableCard variant="card" title="Status conflict" onViewAll={…} onExport={…}>
  <Table>…</Table>
</TableCard>
What the table counts (definition on click)

Pass `info` a ChartDefinition instead of a string and the (i) opens a Popover on click: the sentence, the unit, and what is left out. A string keeps the hover tooltip. Use it where two tables on the page count different things and a reader could add them up.

Status conflict

Per transaction
ProviderTotal transactionsNot reconciled amount USD
dLocal1.234.981$68.190.346 USD
Mercado Pago1.234.981$68.190.346 USD
Adyen1.234.981$68.190.346 USD
Total8.800.000$5.689.190.346 USD
import type { ReactNode } from "react";
import { TableCard } from "@/components/patterns/table-card";
import type { ChartDefinition } from "@/components/patterns/chart-definition";

const counted: ChartDefinition = {
  counts: "Every transaction the provider reported in its settlement file.",
  per: "Per transaction",
  leavesOut: "Transactions still inside the settlement window",
  showPer: true,
};

// children is the kit <Table>.
export function StatusConflict({ children }: { children: ReactNode }) {
  return (
    <TableCard variant="card" title="Status conflict" info={counted}>
      {children}
    </TableCard>
  );
}

Import

Copy this import line at the top of the file where you compose this molecule.

Chrome only — compose the kit Table as children (never wrap it in another bordered box; it ships its own 1px container). Use a TableFooter for the bold Total row.
import { TableCard } from "@/components/patterns/table-card";
import type { ChartDefinition } from "@/components/patterns/chart-definition";

Props

Everything else is forwarded to the underlying elements via ...props.

PropTypeDefaultDescription
titlestring—Card / section title.
variant'section' | 'card''section'section = 24px title, no outer card. card = 16px title inside a padded Card (side-by-side blocks).
infostring | ChartDefinition—The (i) icon next to the title. A string is tooltip text, shown on hover. A ChartDefinition ({ counts, per, leavesOut?, showPer?, labels? }) says what the table counts and opens a Popover on click; with showPer it also prints `per` as a secondary Badge after the icon. Omit to hide.
onViewAll() => void—Shows the 'View all' link in the header.
viewAllLabelstring'View all'Label of the View all link.
onExport() => void—Shows the Export button in the header.
exportLabelstring'Export'Label of the Export button.
actionsReactNode—Extra right-side actions (e.g. a 'By date' dropdown) before View all / Export.
childrenReactNode—The table — a kit <Table> with a TableFooter Total row.
classNamestring—Merged onto the outer element.

Related

Cross-links to the atoms this molecule composes and sibling patterns.

  • Chart cardThe chart-carrying sibling. TableCard is the same chrome for a table body.
  • TableThe children — and the source of the 1px bordered container + the Total footer + the canonical row kebab.
  • ButtonView all (link) + Export (outline).