Color Variants
Showcase of KPI cards with different border colors to indicate status or category.
10 352
GB
10 352
GB
10 352
GB
10 352
GB
import { HvCard, HvCardContent, HvStatusIcon, HvTypography, type HvColor, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer"> <Kpi title="KPI Label" value="10 352" unit="GB" /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; }) { return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvStatusIcon size="xs" variant="default" customIcon={<div className="i-ph-tree-structure" />} /> <span>{title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> </HvCardContent> ); }
Status Icons
Showcase of KPI cards with different status icons.
10 352
GB
10 352
GB
10 352
GB
10 352
GB
import { HvCard, HvCardContent, HvStatusIcon, HvTypography, type HvColor, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer" icon={ <HvStatusIcon variant={colorVariantMap[color]} type="simple" weight="duotone" /> } > <Kpi title="KPI Label" value="10 352" unit="GB" /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; }) { return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvStatusIcon size="xs" variant="default" customIcon={<div className="i-ph-tree-structure" />} /> <span>{title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> </HvCardContent> ); } const colorVariantMap = { positive: "success", warning: "warning", negative: "error", info: "info", } as const;
Selectable Cards
KPI cards with radio selection support—ideal for interactive dashboards or comparisons.
10 352
GB
10 352
GB
10 352
GB
10 352
GB
import { useId, useState } from "react"; import { HvCard, HvCardContent, HvRadio, HvStatusIcon, HvTypography, type HvColor, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { const [selectedIndex, setSelectedIndex] = useState(-1); return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color, i) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer" className="cursor-pointer" onClick={() => setSelectedIndex(i)} selectable selected={i === selectedIndex} > <Kpi title="KPI Label" value="10 352" unit="GB" selected={i === selectedIndex} onSelect={() => setSelectedIndex(i)} /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, selected, onSelect, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; selected: boolean; onSelect: () => void; }) { const titleId = useId(); return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvStatusIcon size="xs" variant="default" customIcon={<div className="i-ph-tree-structure" />} /> <span id={titleId}>{title}</span> </div> <div className="flex justify-between items-center"> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> <HvRadio name="kpi" aria-labelledby={titleId} checked={selected} onClick={onSelect} /> </div> </HvCardContent> ); }
Dynamic KPI Value
Live KPI card that fetches and displays data based on a selected endpoint, including fallback/error handling.
Loading data...
import { useState } from "react"; import useSWR from "swr"; import { HvCard, HvCardContent, HvLoading, HvRadio, HvRadioGroup, HvStatusIcon, HvTypography, } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [endpoint, setEndpoint] = useState("1"); return ( <div className="grid gap-sm"> <HvRadioGroup orientation="horizontal" label="Choose endpoint" value={endpoint} onChange={(evt, val) => setEndpoint(val)} > <HvRadio label="Option 1" value="1" /> <HvRadio label="Option 2" value="2" /> <HvRadio label="Bad fetch" value="error" /> </HvRadioGroup> <HvCard statusColor="info" bgcolor="bgContainer" className="w-300px"> <HvCardContent className="grid gap-sm pb-xs! h-90px"> <KpiData id={endpoint} /> </HvCardContent> </HvCard> </div> ); } function KpiData({ id }: { id: string }) { const { data, isLoading, error } = useSWR(id, fetchData); if (isLoading) { return <HvLoading small label="Loading data..." className="flex-row" />; } if (error) { return ( <div className="flex gap-xs items-center justify-center text-negative"> <HvStatusIcon variant="error" /> <HvTypography>{error.message}</HvTypography> </div> ); } return ( <> <div className="flex items-center gap-xxs"> <HvStatusIcon size="xs" variant="info" /> <span>{data?.title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{data?.value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {data?.unit} </HvTypography> </div> </> ); } function fetchData(id: string) { interface Data { title: string; value: number; unit: string; } return new Promise<Data>((resolve, reject) => { setTimeout(() => { if (Number.isNaN(Number(id))) { reject(new Error("Error fetching data")); return; } resolve({ title: `Title ${id}`, value: Number.parseInt(id.repeat(6), 8) % 10 ** 5, unit: "GB", }); }, 4000); }); }
Small Donut KPI
Small custom donut indicators for displaying percentage values, useful for compact KPI displays. No charts used, just SVG circles.
0%
3%
27%
50%
76%
100%
import { HvTypography, theme } from "@hitachivantara/uikit-react-core"; export default function Demo() { return ( <div className="w-full flex items-center justify-center gap-md"> <SmallDonut value={0} /> <SmallDonut value={3} /> <SmallDonut value={27} /> <SmallDonut value={50} /> <SmallDonut value={76} /> <SmallDonut value={100} /> </div> ); } const SmallDonut = ({ value }: { value: number }) => { const size = 24; const strokeWidth = 2; const margin = 1; const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const pct = Math.max(0, Math.min(100, value)); const fillLength = value === 100 ? circumference : (pct / 100) * circumference; const grayLength = value === 100 ? 0 : circumference - fillLength - (value === 0 ? 0 : 2 * margin); return ( <div className="flex gap-xs items-center"> <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}> <circle cx={size / 2} cy={size / 2} r={radius} fill="none" stroke={theme.colors.positive} strokeWidth={strokeWidth} strokeDasharray={`${fillLength} ${circumference - fillLength}`} /> <circle cx={size / 2} cy={size / 2} r={radius} fill="none" stroke={theme.colors.border} strokeWidth={strokeWidth} strokeDasharray={`${grayLength} ${circumference - grayLength}`} strokeDashoffset={-(fillLength + margin)} /> </svg> <HvTypography variant="captionLabel" style={{ width: size }} className="flex items-center" > {value}% </HvTypography> </div> ); };