Responsive KPI cards with icons, sparklines, trend badges, and skeleton loading. Five style variants — Standard, Compact, Minimal, Filled, Chart. Auto-adjusts from 1–4 columns by screen width. Supports number abbreviation and click events.
All Assets
118
In Use
6
Available
111
Expiring Soon
1
Standard mode
| Property | Type | Default |
|---|---|---|
DataTable of KPI items. Required: Icon, Value, Label, IconBg, IconColor. Optional: ID, PercentChange, PercentLabel, SparklineData (comma-separated numbers), isHidden. | Table | 4 sample cards |
Style"Standard" (value + sparkline footer, icon top-right), "Compact" (88px, icon left, inline %), "Minimal" (88px, no icon), "Filled" (colored bg from IconBg), "Chart" (sparkline hero, value secondary). | Text | "Standard" |
IsLoadingWhen true, hides the data gallery and shows skeleton placeholder cards instead. Use while fetching data. | Boolean | false |
StyleConfigRecord with colors (cardBg, border, text, textMuted, positive, negative, neutral, skeletonBase, skeletonShine), space, radius, type, heights, and abbreviateThreshold tokens. | Record | Light theme tokens |
IconsTable of {Name, SVG} rows. SVG strings use "COLOR" placeholder replaced at runtime by IconColor via Substitute(). Custom icons can be added. | Table | 6 built-in icons |
ColumnsLayoutRecord with Mobile, Tablet, Desktop column counts and MobileBreakpoint (640), TabletBreakpoint (1024), DesktopBreakpoint (1366) widths. | Record | {Mobile:1, Tablet:2, Desktop:4} |
AnimationEffect"Pop" (scale in), "Push" (slide), or "None" (instant). | Text | "Pop" |
| Event | Description |
|---|---|
OnCardClick | Fires when any card is tapped. Receives the full KPI record as ClickedItem (ID, Icon, Value, Label, PercentChange, PercentLabel, IconBg, IconColor, SparklineData). |
Icon top-right in a 44px circle. 190px card. Value at 32px bold, small sparkline in the middle, percent + label footer.
Best for: Full dashboards, detail views
Icon left-aligned in a 44px circle. 88px card. Value and inline percent on one row. No sparkline.
Best for: Above tables, tight layouts
Identical to Compact but no icon — label and value flush-left. Maximum information density.
Best for: Mobile, sidebars, dense UIs
Card background = IconBg, all text = IconColor, icon hidden, border removed. Same data columns — no extra fields needed.
Best for: Hero rows, exec summaries
Sparkline is the hero — full-width, 96px tall, bleeds edge-to-edge. Label + value + inline percent are compact at top. No icon.
Best for: Time-series dashboards, trend focus
Sparklines appear automatically in Standard and Chart modes when SparklineData is non-blank. They are intentionally omitted from Compact, Minimal, and Filled — sparklines need room to communicate. Pass a comma-separated string per record: "10,25,18,42,38,56". Color is determined by PercentChange: positive = green, negative = red, null/0 = gray.
Values ≥ abbreviateThreshold (default 10000) are abbreviated to 10.4K. Values ≥ 1,000,000 become 1.2M. Set abbreviateThreshold: 0 to disable.
Set IsLoading = true to hide the data gallery and show cntSkeletonOverlay — a mirrored gallery with the same WrapCount and TemplateSize rendering placeholder bars. Skeleton cards match the active style.
WrapCount is driven by App.Width vs ColumnsLayout breakpoints. Component height is calculated from visible card count ÷ columns × card height, so it never clips when cards wrap to multiple rows.
All Assets
In Use
Available
cmpKPI.Style = "Chart"
// SparklineData is comma-separated numbers
// sourced from a collection built on load:
App.OnStart:
ClearCollect(colKPITrend,
{
ID: 1,
Label: "All Assets",
Value: 118,
SparklineData: Concat(
colWeeklySnapshots,
Text(AssetCount), ","
)
}
)
// Sparkline color follows PercentChange:
// > 0 → green | < 0 → red | 0 → gray// Show skeleton while data loads
cmpKPI.IsLoading = IsEmpty(colKPIData)
// Typical pattern:
App.OnStart:
Set(varKPILoading, true);
ClearCollect(colKPIData, KPISource);
Set(varKPILoading, false)
cmpKPI.IsLoading = varKPILoadingAll Assets
118
In Use
6
Available
111
Expiring Soon
1
cmpKPI.Data = Table(
{
ID: 1, Icon: "Box", Value: 118,
Label: "All Assets",
PercentChange: 14,
PercentLabel: "vs last month",
IconBg: "#EBF5FF",
IconColor: "#2196F3",
SparklineData: "10,25,42,38,61,70"
},
{
ID: 2, Icon: "Warning", Value: 1,
Label: "Expiring Soon",
PercentLabel: "< 30 days",
IconBg: "#FFEBEE", IconColor: "#F44336",
SparklineData: ""
}
)Total Value
$45.2K
Open Orders
234
// Named formula — auto-refreshing
nfKPIData =
ForAll(
GroupBy(Assets, "Status", "Group"),
{
ID: CountRows(Group),
Icon: Switch(Status,
"Available", "Package",
"In Use", "TrendLines",
"Box"
),
Value: CountRows(Group),
Label: Status,
PercentChange: Round(
(CountRows(Group) - varPrev)
/ varPrev * 100, 0
),
PercentLabel: "vs last month",
IconBg: Switch(Status,
"Available", "#E8F5E9",
"#EBF5FF"
),
IconColor: Switch(Status,
"Available", "#4CAF50",
"#2196F3"
),
SparklineData: ""
}
)
cmpKPI.Data = nfKPIData| Field | Type | Required | Notes |
|---|---|---|---|
| ID | Number | Recommended | Unique identifier used for OnCardClick context. |
| Icon | Text | Yes | Name matching an Icons table entry e.g. "Box", "TrendLines", "Warning". |
| Value | Number | Yes | Main KPI number. Auto-abbreviated if ≥ abbreviateThreshold. |
| Label | Text | Yes | Card title. Labels containing value/price/cost auto-format Value as currency. |
| PercentChange | Number | No | Trend %. Positive = green ↑, negative = red ↓. Blank to hide. Also drives sparkline color. |
| PercentLabel | Text | No | Footer sub-text e.g. "vs last month". Blank to hide. |
| IconBg | Text | Yes | Hex background for icon circle. In Filled, becomes the entire card background. |
| IconColor | Text | Yes | Hex color for SVG stroke. In Filled, becomes all text and percent color. |
| SparklineData | Text | No | Comma-separated numbers e.g. "10,25,18,42". Shown in Standard and Chart only. Empty string hides sparkline. |
| isHidden | Boolean | No | Set true to hide a card without removing it from the data source. |
20 controls — 5 Text, 8 Classic/Button, 4 Image, 4 GroupContainer, 1 Gallery (×2 for skeleton).
7 input properties — Data, Style, IsLoading, StyleConfig, Icons, ColumnsLayout, AnimationEffect.
1 event — OnCardClick.
Default size — Parent.Width × dynamic height. Card height: 88px (Compact/Minimal) or 190px (Standard/Filled/Chart).
cmpKPI (Parent.Width × dynamic height)
├── cntStatsRow (GroupContainer — AutoLayout, Visible=!IsLoading)
│ └── galKPIs (Gallery — vertical, WrapCount by breakpoint)
│ └── conKPICard (GroupContainer — ManualLayout, DropShadow.Light)
│ ├── txtLabel (ModernText — uppercase, 12px semibold)
│ ├── cntValueRow (GroupContainer — AutoLayout horizontal)
│ │ ├── txtValue (ModernText — Bold, FillPortions=1, abbreviation formula)
│ │ └── txtPercentInline (Text — Compact/Minimal/Chart only)
│ ├── imgSparkline (Image — SVG, clr resolved from PercentChange, Standard+Chart)
│ ├── cntKPIFooter (GroupContainer — Standard+Filled only)
│ │ ├── txtFooterPercent (ModernText — ↑/↓, semibold)
│ │ └── txtFooterSub (ModernText — "vs last month", 12px)
│ ├── btnIconBg (Classic/Button — circle, hidden in Filled/Minimal/Chart)
│ ├── imgIconKPI (Image — inline SVG, hidden in Filled/Minimal/Chart)
│ └── btnCardOverlay (Classic/Button — transparent, OnSelect→OnCardClick)
│
└── cntSkeletonOverlay (GroupContainer — Visible=IsLoading)
└── galSkeleton (Gallery — Sequence(8), same WrapCount+TemplateSize)
└── conSkeletonCard (GroupContainer)
├── btnSkeletonLabel (Classic/Button — 10px bar)
├── btnSkeletonValue (Classic/Button — 24px bar)
├── btnSkeletonChart (Classic/Button — 96px full-width, Chart only)
├── btnSkeletonFooter (Classic/Button — 10px bar, Standard only)
└── btnSkeletonIcon (Classic/Button — 44px circle, Standard/Compact/Filled)
Sparkline visibility matrix:
Standard → imgSparkline at Y=106, H=36, W=card-48, X=20
Chart → imgSparkline at Y=card.H-96, H=96, W=card (edge-to-edge)
Compact / Minimal / Filled → imgSparkline hiddenOne email when something new ships. Unsubscribe anytime.
Use the toolbar to format · or type markdown directly