mds components
mds:chart
Monochrome dashboard charts, server-rendered as SVG — an open answer to a Flux Pro component.
A family of dashboard visualizers in one ink: rounded splines, pill bars, arc dials. Everything renders on the server — no chart library, no JSON payload, no script. The design follows Amicro's Mono Charts (MIT). Flux Pro's flux:chart is a client-side line-chart toolkit; mds:chart is a different, batteries-included take for static dashboard cards.
<mds:chart
label="Spline dynamics"
badge="Line"
:value="84"
unit="k nodes"
footer-start="Rounded caps"
footer-end="84k peak"
class="w-full max-w-sm"
>
<mds:chart.line :data="[24, 45, 38, 65, 52, 84]" :labels="['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']" />
</mds:chart>
The card and the stage
Two layers, both optional. mds:chart is the card — label, badge, big stat, footer. The mds:chart.* stages draw the actual chart and work anywhere on their own: inside a flux:card, a table cell, or bare. The ink is currentColor, so one text utility recolors a whole chart.
<div class="w-full max-w-sm space-y-4">
<mds:chart.line :data="[18, 34, 72, 89, 64, 48]" :labels="['02', '06', '10', '14', '18', '22']" area :dots="false" />
<mds:chart.line :data="[18, 34, 72, 89, 64, 48]" area :dots="false" :axis="false" class="text-accent" />
</div>
Line
The line is a monotone spline — smooth, but it never overshoots the data. baseline adds a dashed comparison series on the same axis, area pours a soft gradient under the curve, and curve="straight" switches to plain segments.
<mds:chart
label="Weekly sessions"
badge="vs last week"
:value="84"
unit="k"
footer-start="Dashed: last week"
footer-end="84k peak"
class="w-full max-w-sm"
>
<mds:chart.line
:data="[24, 45, 38, 65, 52, 84]"
:baseline="[18, 32, 29, 48, 41, 62]"
:labels="['Sat', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu']"
area
/>
</mds:chart>
Bars
Bars are pills — fully rounded ends, one tone. A secondary series renders as a faint twin next to each bar. When an item is an array, its values stack: solid base, lighter tones upward, only the stack's outer ends rounded.
<div class="grid w-full gap-4 sm:grid-cols-2">
<mds:chart label="Pill pillars" badge="Grouped" :value="422" unit="units">
<mds:chart.bars
:data="[45, 78, 62, 95, 88, 54]"
:secondary="[25, 40, 30, 55, 50, 28]"
:labels="['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']"
/>
</mds:chart>
<mds:chart label="Stacked tones" badge="3 layers" :value="160" unit="cumulative">
<mds:chart.bars
:data="[[30, 25, 20], [45, 35, 25], [60, 40, 30], [75, 50, 35]]"
:labels="['Q1', 'Q2', 'Q3', 'Q4']"
/>
</mds:chart>
</div>
Funnel rows
horizontal switches to HTML rows — label, pill track, value. Rows follow the page direction (they grow from the right in RTL), which makes them the funnel of choice for Persian checkouts.
<mds:chart label="Stage funnel" badge="Pipeline" value="24%" unit="conversion" class="w-full max-w-sm">
<mds:chart.bars horizontal :data="[100, 68, 42, 24]" :labels="['Visits', 'Signup', 'Active', 'Paid']" />
</mds:chart>
Donut
Rounded segments separated by real gaps, shaded down a fixed tone ladder in segment order. The keys of data are the labels; the legend inherits each segment's tone. value and label fill the center.
<mds:chart label="Rounded donut" badge="Soft arc caps" value="100%" unit="allocation" class="w-full max-w-sm">
<mds:chart.donut
:data="['Core engine' => 45, 'UI layer' => 30, 'Assets' => 15, 'Other' => 10]"
value="100%"
label="Mono arc"
/>
</mds:chart>
Gauge and bullet
The gauge is a 240° dial with pill ends over a faint track. The bullet rows compare a value against a target — the marker is the one accent-colored element in the family, sitting on --color-accent.
<div class="grid w-full gap-4 sm:grid-cols-2">
<mds:chart label="Speedometer arc" badge="Gauge" value="84%" unit="performance">
<mds:chart.gauge :value="84" label="Target met" />
</mds:chart>
<mds:chart label="Bullet target" badge="Benchmark" :value="3" unit="targets">
<mds:chart.bullet :items="[
['label' => 'Throughput', 'value' => 82, 'target' => 75],
['label' => 'Latency', 'value' => 65, 'target' => 80],
['label' => 'Uptime', 'value' => 95, 'target' => 90],
]" />
</mds:chart>
</div>
Radar
A polygon web: four grid rings, one spoke per key, the shape filled at 15% ink.
<mds:chart label="Polygon web" badge="Radar" :value="85" unit="score" class="w-full max-w-sm">
<mds:chart.radar :data="['Speed' => 90, 'Memory' => 75, 'Scale' => 85, 'Latency' => 95, 'IOPS' => 80]" />
</mds:chart>
Activity heatmap
A contribution grid: values grade into five tones of the ink, columns follow the page direction, and hovering a tile reports its value in the callout row (Alpine, with a title fallback). color="accent" swaps the ladder onto the accent color.
<mds:chart label="Activity heatmap" badge="14 weeks" :value="807" unit="contributions" class="w-full max-w-md">
<mds:chart.heatmap
:data="array_map(fn ($i) => ($i * 7) % 13, range(1, 98))"
:labels="['Jan', 'Feb', 'Mar']"
color="accent"
/>
</mds:chart>
KPI card recipe
There is no KPI component — it is the card doing what it already does: a stat, a delta (a leading minus turns it red), and a bare mds:chart.sparkline, which also works inline in tables and lists.
<mds:chart label="KPI stat card" badge="Metric" value="$48,920" delta="+14.2%" footer-start="Rounded sparkline" footer-end="Monthly revenue" class="w-full max-w-sm">
<mds:chart.sparkline :data="[30, 45, 35, 60, 50, 85, 75, 95]" area class="h-16" />
</mds:chart>
Persian output
config('mds.persian_digits') — on by default in a real app, off in these docs — switches every digit the family renders: the card's stat and delta, axis ticks, bullet readouts, the heatmap's callout and its built-in hover hint. :fa="true" on the card flows into the stages inside it. The SVG plot plane itself never mirrors; the HTML stages (funnel rows, bullet, heatmap) follow the page direction.
<div class="grid w-full gap-4 sm:grid-cols-2">
<mds:chart label="فروش ماهانه" badge="تومان" :value="48920" unit="هزار" delta="+14.2%" footer-start="شش ماه اخیر" footer-end="اوج در مرداد" :fa="true">
<mds:chart.line :data="[24, 45, 38, 65, 52, 84]" :labels="['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور']" area />
</mds:chart>
<mds:chart label="قیف فروش" badge="۴ مرحله" value="۲۴٪" unit="نرخ تبدیل" :fa="true">
<mds:chart.bars horizontal :data="[100, 68, 42, 24]" :labels="['بازدید', 'سبد خرید', 'پرداخت', 'خرید']" />
</mds:chart>
</div>
Reference
mds:chart
| Prop | Description |
|---|---|
| label | Uppercase kicker line above the stat. |
| badge | Small pill next to the label. |
| value | The big stat. Numbers get thousands separators; strings ("84%") keep their shape with localized digits. |
| unit | Faint suffix after the stat. |
| delta | Change readout after the stat — green, or red when it starts with a minus. |
| footer-start | Muted footer text on the leading edge. |
| footer-end | Emphasized footer text on the trailing edge. |
| fa | Persian digits for the stat and delta — inherited by the stages inside. Default: config('mds.persian_digits'). |
| Slot | Description |
|---|---|
| header | Extra content on the header's trailing edge — filters, a toggle. |
| footer | Replaces the two footer props. |
| default | The stage(s). |
mds:chart.line
| Prop | Description |
|---|---|
| data | Array of numbers. |
| labels | Category labels under the plot, one per point. |
| baseline | Second series, drawn dashed and faint on the same axis. |
| area | Gradient fill under the curve. Default: false. |
| dots | Point markers with a stage-colored halo. Default: true. |
| curve | Options: smooth (monotone spline), straight. Default: smooth. |
| axis | Y ticks, grid lines and x labels. Default: true. |
| max | Pins the axis ceiling. Default: a "nice" ceiling above the data. |
| width | ViewBox width. Default: 360. |
| height | ViewBox height. Default: 170. |
| fa | Persian digits on ticks and labels. Default: the enclosing card's, else the config. |
mds:chart.bars
| Prop | Description |
|---|---|
| data | Array of numbers — or arrays, which stack into layers (bottom first). |
| labels | Category labels. |
| secondary | Faint second series next to each bar. |
| horizontal | HTML rows that follow the page direction — the funnel layout. Default: false. |
| axis | Y ticks, grid lines and x labels. Default: true. |
| max | Pins the scale ceiling. |
| width | ViewBox width. Default: 360. |
| height | ViewBox height. Default: 170. |
| fa | Persian digits. Default: the enclosing card's, else the config. |
mds:chart.donut
| Prop | Description |
|---|---|
| data | Assoc array — labels to values. |
| value | Center readout. Default: the values' sum. |
| label | Small line under the center readout. |
| legend | Tone-matched legend under the ring. Default: true. |
| size | ViewBox size. Default: 160. |
| thickness | Ring thickness. Default: 22. |
| fa | Persian digits. Default: the enclosing card's, else the config. |
mds:chart.gauge
| Prop | Description |
|---|---|
| value | The dialed value. |
| max | Full-dial value. Default: 100. |
| label | Small line under the readout. |
| decimals | Decimals in the readout. Default: 0. |
| fa | Persian digits. Default: the enclosing card's, else the config. |
mds:chart.radar
| Prop | Description |
|---|---|
| data | Assoc array — axis labels to values. |
| max | The web's outer ring value. Default: 100. |
| fa | Persian digits in labels. Default: the enclosing card's, else the config. |
mds:chart.bullet
| Prop | Description |
|---|---|
| items | Rows: [['label' => ..., 'value' => ..., 'target' => ...], ...] — target optional. |
| max | Full-track value. Default: 100. |
| unit | Suffix in the readouts. Default: %. |
| fa | Persian digits. Default: the enclosing card's, else the config. |
mds:chart.heatmap
| Prop | Description |
|---|---|
| data | Array of counts, column-major — every rows values are one column. |
| rows | Cells per column. Default: 7. |
| labels | Labels spread across the top — usually months. |
| color | Options: accent. Default: the ink. |
| unit | Word in the hover callout. Default: "items" / مورد by language. |
| callout | The hover-readout row (needs Alpine; tiles keep a title either way). Default: true. |
| fa | Persian digits and built-in strings. Default: the enclosing card's, else the config. |
mds:chart.sparkline
A bare <svg> that stretches to any box (strokes stay 2px crisp) — size it with width/height utilities.
| Prop | Description |
|---|---|
| data | Array of numbers — the sparkline auto-fits its own range. |
| area | Gradient fill under the curve. Default: false. |
| curve | Options: smooth, straight. Default: smooth. |
| width | ViewBox width. Default: 120. |
| height | ViewBox height. Default: 28. |