Menu
Overview
General
ButtonBadgePillsAvatarBrand lockupLayout
ContainerGrid & RowSurface & text utilitiesApp shellPage headerData Display
CardTableChartsCalendarStatsBulletsLeadersLogo wallCollapse & DisclosureTimelineValue chainDescriptionsFeedback
AlertProgressModalSlide-overSkeletonEmpty stateForms
Field & InputDropdownLead form & CTA rowDate pickerFilter barNavigation
NavSide navigationTop navigationApp switcherUser menuStepperTabsBreadcrumbPaginationTemplates
Sign-in pageSigned-out pageSettings layoutTheming
Browser tabProduct themesData 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<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.
| Product | Volume | Share |
|---|---|---|
| novapay | 2,480 | 62% |
| novabank | 960 | 24% |
| novastore | 560 | 14% |
<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><div class="chartframe chartframe--short" id="mixchart"></div></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:volumeChartfor composition plus trajectory,donutChartfor 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
.chartframeto 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.
/* 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); }