Guide: Dashboards, tables and charts

## Tables

Tables

  • Use table, thead, tbody, tr, th and td. Give the cells of one column the same padding, alignment and text style.
  • Give a fixed column its width on the th: w-30 is 120 px. The other columns share the rest.
  • Make rows 40 to 48 px high, with 12 to 16 px of padding in the cells. Use a fill on alternate rows only in a dense table.
  • Align numbers to the right, with tabular-nums. Write dates in one format, such as 3 Oct 2026.
  • Write each row out. For many rows, build the TSX in the execute snippet with .map().
  • Show status with a Badge component that has a variant for each status.

See the recipe in layout.md.

Dashboards

  • Put a row of metric cards at the top: instances of one Metric component in grid grid-cols-4 gap-4.
  • Put charts and tables under it in a grid of two columns: grid grid-cols-[2fr_1fr] gap-4.
  • Give each card a title, and put the time range at the top right.

See the recipe in layout.md.

Charts

Make a bar chart from flex items. Give the chart a fixed height, and give each bar a height in percent. Put the labels in a second row with the same columns:

<div data-name="Bar chart" className="flex flex-col gap-2">
  <div className="flex h-48 items-end gap-2">
    <div className="h-[40%] flex-1 rounded-t bg-accent/30" />
    <div className="h-[65%] flex-1 rounded-t bg-accent/30" />
    <div className="h-[50%] flex-1 rounded-t bg-accent/30" />
    <div className="h-[85%] flex-1 rounded-t bg-accent" />
    <div className="h-[70%] flex-1 rounded-t bg-accent/30" />
  </div>
  <div className="flex gap-2 text-center text-xs text-muted-foreground">
    <span className="flex-1">Mon</span>
    <span className="flex-1">Tue</span>
    <span className="flex-1">Wed</span>
    <span className="flex-1">Thu</span>
    <span className="flex-1">Fri</span>
  </div>
</div>

Make a line chart with an svg and a polyline. preserveAspectRatio="none" stretches it to its box, and vectorEffect="non-scaling-stroke" keeps the line width:

<svg
  viewBox="0 0 300 100"
  preserveAspectRatio="none"
  aria-hidden="true"
  className="h-40 w-full text-accent"
>
  <polyline
    points="0,80 50,62 100,70 150,44 200,50 250,24 300,16"
    fill="none"
    stroke="currentColor"
    strokeWidth={2}
    vectorEffect="non-scaling-stroke"
  />
</svg>

Make a donut with two circles. A radius of 15.9155 gives a circumference of 100, so strokeDasharray="70 30" shows 70 percent:

<svg viewBox="0 0 36 36" aria-hidden="true" className="size-32 text-accent">
  <circle
    cx="18"
    cy="18"
    r="15.9155"
    fill="none"
    stroke="currentColor"
    strokeOpacity={0.15}
    strokeWidth={4}
  />
  <circle
    cx="18"
    cy="18"
    r="15.9155"
    fill="none"
    stroke="currentColor"
    strokeWidth={4}
    strokeDasharray="70 30"
    transform="rotate(-90 18 18)"
  />
</svg>
  • Do not put labels on a chart by hand with absolute positions. Let flex align them with the bars.
  • Use the accent colour for the data that is important, and a light tint of it for the rest.