Portable dashboard component library for Frappe. Metric tiles, charts, status pills, themed end-to-end. Drop it into any Frappe bench and your custom dashboards stop looking like ERPNext.
cd $PATH_TO_YOUR_BENCH
bench get-app https://github.com/AXKNETWORK/dashboard-kit
bench --site <yoursite> install-app dashboard_kitThat's it. Tokens, themes, components and helpers are now available everywhere on the bench.
In your app's Python:
import frappe
from dashboard_kit.helpers import (
dashboard_response, hero_tile, kpi_tile, dual_line_chart,
donut_chart, feed, filter_bar,
)
from dashboard_kit.helpers.deltas import mtd_delta, trailing_delta
from dashboard_kit.helpers.currency import money
@frappe.whitelist()
def platform_admin():
return dashboard_response(
title="Platform Administration",
filters=filter_bar(period="30d", segments=["All", "Coffee", "Cocoa"]),
hero=hero_tile(
label="Platform Users",
value=frappe.db.count("User"),
delta=mtd_delta("User"),
sub=[
{"label": "DAU 24h", "value": 14},
{"label": "Live escrows", "value": 308},
{"label": "Escrow $ live", "formatted_value": money(92_000_000, "USD", compact=True)},
],
status="healthy",
),
kpis=[
kpi_tile("Live escrows", 308, status="normal"),
kpi_tile("Live escrow $",
formatted_value=money(92_000_000, "USD", compact=True),
delta=trailing_delta("Escrow Contract", days=14, value_field="amount")),
kpi_tile("AI spend MTD",
formatted_value=money(0, "USD"),
status="optimal"),
],
main=dual_line_chart(
"User growth · 14d vs prior 14d",
current=last_14d_user_signups(),
prior=prior_14d_user_signups(),
),
side=donut_chart(
"Volume by corridor",
data=[
{"label": "Kigali → Hamburg", "value": 1_701_700},
{"label": "Mombasa → Antwerp", "value": 980_400},
{"label": "Other", "value": 320_500},
],
),
rail=[
feed("Recent platform events", recent_events()),
],
)In your workspace HTML / page template:
<div id="platform-admin"></div>
<script>
// pick the theme on entry — once per session, persisted in localStorage
dk.theme.set('axk');
dk.mountFromEndpoint('#platform-admin', 'my_app.api.dashboards.platform_admin');
</script>That's the whole loop: Python builds the config, JS mounts it.
Four themes ship out of the box:
| Theme | Look | Reference |
|---|---|---|
default |
Neutral baseline | clean for any brand |
axk |
Smart-energy green | AXK Network |
rovify |
Clean POS blue/white | Rovify |
justly |
Monochrome, terminal-adjacent | GetJustly |
Switch at runtime: dk.theme.set('axk'). Persisted in localStorage. Light/dark mode flips with dk.theme.mode('light' | 'dark' | 'auto').
Copy dashboard_kit/public/css/themes/_starter.css to your_app/public/css/themes/your_brand.css, edit the brand-color block (--dk-primary and friends), and add it to your app's app_include_css in hooks.py. Done — dk.theme.set('your_brand') flips your whole bench to it.
Every component reads from CSS custom properties (--dk-*); there is no hard-coded color anywhere in components.css.
Metric tiles — hero_tile, kpi_tile, trend_tile, progress_tile. Status pills, delta badges, sparklines, sub-metrics.
Charts — line_chart, dual_line_chart (current vs prior), donut_chart (with center value), bar_chart (vertical/horizontal/stacked), sparkline.
Layout — filter bar (period + segment), KPI grid (responsive 4/3/2/1), section headers, two-column with right rail, collapsible cards.
Data display — detail table (sortable, pill cells), activity feed (timeline with avatars), top-N ranked list (with bars).
Status & feedback — 5-level pills (optimal, healthy, normal, attention, critical), delta badges (up/down/flat), live-data indicator, loading skeletons, empty states.
Domain cards (coming, AXK-relevant) — anchor-proof card (Merkle root + tx hash + explorer link), money-flow card, pipeline-status card, audit-trail card.
tokens.css is the source of truth — every visual variable is a CSS custom property. Themes override only the variables they need. components.css reads tokens; nothing else touches color or spacing values directly. The JS layer is a tiny renderer (core.js) plus per-component modules that register themselves into a registry via dk.register(type, fn). Python helpers/ returns plain dicts with a type field; the JS renderer looks up the type, calls the registered function, returns a DOM node. Pure JSON contract between Python and JS — no Jinja templates in the rendering hot-path. Re-skinning a 50-screen Frappe bench is changing one CSS file.
docs/components.md— every component, with screenshots, props, and a copy-pasteable exampledocs/theming.md— how to make a themedocs/architecture.md— the JSON contract spec, render pipeline, extension pointsdocs/recipes.md— common dashboard patterns end-to-endexamples/— three reference dashboards (AXK, Rovify, Justly) you can read or import as fixtures
MIT.