Novus Technologies Novus Technologies design kit v0.9.0
Menu

Data Display

Charts

Token-fed D3 charts: composition bars with a trajectory line, share donut, and the table that carries the same numbers.

.chartframe · .chartcard · .charttip · composes .table

Volume by hour

today, by product
Chart language: composition in the bars, trajectory on the line
<div class="card chartcard">
  <div class="chartcard__head"><h3>Volume by hour</h3><span class="muted">today, by product</span></div>
  <svg class="chart" viewBox="0 0 420 170" role="img" aria-label="Example: stacked volume bars with a total trajectory line">
    <g stroke="var(--border)" stroke-dasharray="4 4">
      <line x1="30" x2="410" y1="40" y2="40"/><line x1="30" x2="410" y1="80" y2="80"/><line x1="30" x2="410" y1="120" y2="120"/>
    </g>
    <path d="M40,150 V100 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M40,150 V134 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M86,150 V82 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M86,150 V129 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M132,150 V95 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M132,150 V133 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M178,150 V68 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M178,150 V125 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M224,150 V60 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M224,150 V123 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M270,150 V84 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M270,150 V130 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M316,150 V92 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M316,150 V132 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M362,150 V73 a6,6 0 0 1 6,-6 h18 a6,6 0 0 1 6,6 V150 Z" fill="var(--blue-500)"/><path d="M362,150 V127 h30 V150 Z" fill="var(--green-500)"/>
    <path d="M55,88 C110,70 160,96 210,58 S320,44 395,52" fill="none" stroke="var(--text-secondary)" stroke-width="2"/>
  </svg>
</div>

Product mix

share of today's volume

<div class="chartframe chartframe--short" id="mixchart"></div> is where novusChart.donutChart draws. The table below carries the same numbers.

ProductVolumeShare
novapay2,48062%
novabank96024%
novastore56014%
The markup: a frame for the chart, a table for the same numbers
<div class="card chartcard">
  <div class="chartcard__head"><h3>Product mix</h3><span class="muted">share of today's volume</span></div>
  <p class="muted" style="margin:0"><code>&lt;div class="chartframe chartframe--short" id="mixchart"&gt;&lt;/div&gt;</code> is where <code>novusChart.donutChart</code> draws. The table below carries the same numbers.</p>
  <div class="tablewrap">
    <table class="table">
      <thead><tr><th>Product</th><th class="num">Volume</th><th class="num">Share</th></tr></thead>
      <tbody>
        <tr><td>novapay</td><td class="num">2,480</td><td class="num">62%</td></tr>
        <tr><td>novabank</td><td class="num">960</td><td class="num">24%</td></tr>
        <tr><td>novastore</td><td class="num">560</td><td class="num">14%</td></tr>
      </tbody>
    </table>
  </div>
</div>

Usage

  • Charts are drawn with D3 (the kit's charting standard) through novus-chart.js: volumeChart for composition plus trajectory, donutChart for share. See them live in the Admin Kit demos.
  • Every colour and font is read from the tokens at render time, so a theme change redraws the chart correctly and no colour literal ever enters the code.
  • The locked rules: soft dashed gridlines at low alpha, rounded bars with constrained thickness, no axis borders or tick marks, a dark rounded tooltip, one composition read and one trajectory read on the primary series.
  • Give the chart a .chartframe to size it and a table of the same numbers beside or under it. The table is the answer for people without scripting, and for anyone who wants the figures.
  • Marks answer hover and keyboard focus with the same tooltip; series colours are the product accents, never traffic-light colours, because volume is data and not a judgement.

CSS

These rules ship in the console pattern layer, admin-kits/shared/novus-admin.css, which this site and every Admin Kit flavor load after tokens.css. Copy the whole layer, or only the sections below; every value is a token.

@pattern charts
/* D3 charts under the locked Novus rules: the frame owns sizing and the
   tooltip, the chart owns nothing but tokens (constitution 1.12.0, VII). */
.chartframe { position: relative; width: 100%; min-width: 0; height: 22rem; }
.chartframe--short { height: 14rem; }
.chart { display: block; overflow: visible; }
.chart text { font-size: var(--text-xs); font-family: var(--font-sans); }
.chart__centre { font-size: var(--text-lg); font-weight: var(--weight-bold); }
.chart [tabindex]:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.charttip { position: absolute; z-index: 20; min-width: 9rem; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--neutral-900); color: var(--neutral-0); font-size: var(--text-xs); line-height: var(--lh-xs); pointer-events: none; box-shadow: var(--shadow-2); display: flex; flex-direction: column; gap: calc(var(--space-1) / 2); }
.charttip[hidden] { display: none; }
.charttip b { font-size: var(--text-sm); }
.charttip span { display: flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.charttip i { width: 0.5rem; height: 0.5rem; border-radius: var(--radius-pill); flex: 0 0 auto; }
.charttip__total { border-top: 1px solid var(--neutral-700); margin-top: calc(var(--space-1) / 2); padding-top: calc(var(--space-1) / 2); }
.statgrid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); align-items: start; margin-bottom: var(--space-6); }
.chartcard { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.chartcard__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-1) var(--space-3); }
.chartcard__head h2, .chartcard__head h3 { margin: 0; }
.tabledetails > summary { display: inline-flex; align-items: center; min-height: 2.75rem; color: var(--accent-text); cursor: pointer; font-size: var(--text-sm); }
.tabledetails > summary::-webkit-details-marker { display: none; }
.tabledetails[open] > summary { margin-bottom: var(--space-2); }