Skip to content
NNeumorphism UI
DocsStylingChartsTemplates
—
EN
English한국어简体中文日本語
EXACT DATA · VISUAL ENCODING

Charts that do not hide the data.

Visualize product analytics and operational signals while every recipe keeps an exact-data table beside the marks.

PRODUCT ANALYTICS

Product analytics

Revenue, channel and conversion share one analytics model and range control.

Illustrative data · September 2026 · USD. No live service is connected.

Revenue against target

Daily revenue, displayed as currency. The dashed line is the target.

Revenue: $60,644.00 · Target: $75,810.00

View exact data
Revenue against target
DateRevenueTarget
2026-09-17$3,682.00$5,064.00
2026-09-18$3,842.00$5,118.00
2026-09-19$4,292.00$5,172.00
2026-09-20$4,542.00$5,226.00
2026-09-21$3,640.00$5,280.00
2026-09-22$4,040.00$5,334.00
2026-09-23$4,142.00$5,388.00
2026-09-24$4,642.00$5,442.00
2026-09-25$4,842.00$5,496.00
2026-09-26$3,890.00$5,550.00
2026-09-27$4,340.00$5,604.00
2026-09-28$4,550.00$5,658.00
2026-09-29$5,000.00$5,712.00
2026-09-30$5,200.00$5,766.00

Acquisition mix

Channel totals in the selected period. Every bar starts at zero.

Visits: 23,778

View exact data
Acquisition mix
Acquisition channelVisitsConverted visitsConversion rate
Organic9,4703864.08%
Direct8,0114205.24%
Referral6,2974046.42%

Visit conversion

Converted visits divided by visits. The vertical scale stays at 0–100%.

5.09% · 1,210 / 23,778

View exact data
Visit conversion
DateVisitsConverted visitsConversion rate
2026-09-171,557734.69%
2026-09-181,547774.98%
2026-09-191,622865.3%
2026-09-201,612915.65%
2026-09-211,602724.49%
2026-09-221,677804.77%
2026-09-231,667834.98%
2026-09-241,742935.34%
2026-09-251,732975.6%
2026-09-261,722774.47%
2026-09-271,797864.79%
2026-09-281,787915.09%
2026-09-291,8621005.37%
2026-09-301,8521045.62%

Missing days are gaps, not zero. A zero-visit conversion rate is undefined (—).

Operational charts

Separate operational signals from product analytics: builds, latency, releases, delivery and install traces.

Build duration

Paired cold and cached build runs in seconds.

Mean cold 210.2 s · cached 103.7 s · 2/6 cold runs over budget

View build data
Build duration data
BuildCold (s)Cached (s)Saved (s)
B1210108102
B219510293
B3238114124
B4226109117
B520496108
B61889395

Service latency

Daily p50 and p95 response time against a p95 budget.

Worst p95 470 ms · 3/7 periods over budget

View latency data
Service latency data
Periodp50 (ms)p95 (ms)Budget
Mon80210Within
Tue76195Within
Wed95340Over
Thu130470Over
Fri100330Over
Sat82240Within
Sun79215Within

Release activity

New installs and updates by release. Counts are events, not unique users.

Total activity 2,350 · update share 32.8%

View release data
Release activity data
ReleaseInstallsUpdatesUpdate share
R11486229.5%
R223210431.0%
R31968831.0%
R428413231.7%
R534117634.0%
R637820935.6%

Delivery capacity

Planned and delivered item counts. This is not a productivity score.

Planned 140 · delivered 140 · net variance +0

View delivery data
Delivery capacity data
PeriodPlannedDeliveredVariance
S13228-4
S23639+3
S33431-3
S43842+4

Installation diagnostics

Sequential stage durations for one installation trace.

Total 1260 ms · longest stage Download (840 ms)

View installation data
Installation diagnostic data
StageDurationShare
Resolve120 ms9.5%
Download840 ms66.7%
Transform210 ms16.7%
Write90 ms7.1%
REGISTRY SOURCE

Installation reference

Install only the recipes you need as source. Expand implementation and Registry source when you need them.

01Product analytics

Revenue against target

Daily revenue with exact values and shared range controls.

@neumorphism-ui/chart-revenue
Usage
Usage
"use client";
import { RevenueChart } from "@/components/blocks/revenue-chart";
import { summarizeAnalytics, type AnalyticsRecord } from "@/lib/analytics-model";

export function Example({ records }: { records: AnalyticsRecord[] }) {
  return <RevenueChart data={summarizeAnalytics(records, 14)} locale="en" />;
}
Source
@components/blocks/revenue-chart.tsx
"use client";

import * as React from "react";
import { Area, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from "recharts";
import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Checkbox } from "@/components/ui/checkbox";
import type { AnalyticsSummary } from "@/lib/analytics-model";
import { analyticsCopy, analyticsFormats, type AnalyticsLocale } from "@/lib/analytics-copy";

export function RevenueChart({ data, locale = "en", currency = "USD" }: {
  data: AnalyticsSummary; locale?: AnalyticsLocale; currency?: string;
}) {
  const [showTarget, setShowTarget] = React.useState(true);
  const t = analyticsCopy[locale];
  const f = analyticsFormats(locale, currency);
  return <ChartContainer
    data-chart="revenue" title={t.revenueTitle} description={t.revenueBody}
    summary={data.observedDays ? <>{t.revenue}: {f.money(data.totals.revenueCents)} · {t.target}: {f.money(data.totals.targetCents)}</> : t.empty}
    actions={<label className="inline-flex items-center gap-2 text-xs"><Checkbox checked={showTarget} onCheckedChange={value => setShowTarget(value === true)} />{t.targetToggle}</label>}
    tableLabel={t.table} empty={data.observedDays === 0} emptyLabel={t.empty}
    table={<table><caption className="sr-only">{t.revenueTitle}</caption><thead><tr><th scope="col">{t.date}</th><th scope="col">{t.revenue}</th><th scope="col">{t.target}</th></tr></thead><tbody>{data.points.map(point => <tr key={point.date}><th scope="row">{point.date}</th><td>{f.money(point.revenueCents)}</td><td>{f.money(point.targetCents)}</td></tr>)}</tbody></table>}
  >
    <ComposedChart data={data.points} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
      <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
      <XAxis dataKey="date" tickFormatter={(value: string) => value.slice(5)} minTickGap={28} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <YAxis domain={[0, "auto"]} width={72} tickFormatter={(value: number) => f.compactMoney(value)} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <ChartTooltip formatter={value => f.money(typeof value === "number" ? value : null)} />
      <Area type="linear" dataKey="revenueCents" name={t.revenue} stroke="var(--primary)" fill="var(--primary)" fillOpacity={0.1} strokeWidth={2.5} connectNulls={false} isAnimationActive={false} />
      {showTarget && <Line type="linear" dataKey="targetCents" name={t.target} stroke="var(--muted-foreground)" strokeWidth={2} strokeDasharray="6 4" dot={false} connectNulls={false} isAnimationActive={false} />}
    </ComposedChart>
  </ChartContainer>;
}
02Product analytics

Acquisition mix

Channel contribution without hiding the source totals.

@neumorphism-ui/chart-channel
Usage
Usage
"use client";
import { ChannelChart } from "@/components/blocks/channel-chart";
import { summarizeAnalytics, type AnalyticsRecord } from "@/lib/analytics-model";

export function Example({ records }: { records: AnalyticsRecord[] }) {
  return <ChannelChart data={summarizeAnalytics(records, 14)} locale="en" />;
}
Source
@components/blocks/channel-chart.tsx
"use client";

import * as React from "react";
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Select, SelectItem } from "@/components/ui/select";
import type { AnalyticsSummary } from "@/lib/analytics-model";
import { analyticsCopy, analyticsFormats, type AnalyticsLocale } from "@/lib/analytics-copy";

export function ChannelChart({ data, locale = "en" }: { data: AnalyticsSummary; locale?: AnalyticsLocale }) {
  const [metric, setMetric] = React.useState<"visits" | "convertedVisits">("visits");
  const t = analyticsCopy[locale];
  const f = analyticsFormats(locale);
  const label = metric === "visits" ? t.visits : t.conversions;
  return <ChartContainer
    data-chart="channels" title={t.channelsTitle} description={t.channelsBody}
    summary={data.observedDays ? <>{label}: {f.number(data.totals[metric])}</> : t.empty}
    actions={<label className="grid max-w-full gap-1.5 text-xs">{t.metric}<Select value={metric} onChange={event => setMetric(event.target.value === "visits" ? "visits" : "convertedVisits")}><SelectItem value="visits">{t.visits}</SelectItem><SelectItem value="convertedVisits">{t.conversions}</SelectItem></Select></label>}
    tableLabel={t.table} empty={!data.channels.length} emptyLabel={t.empty}
    table={<table><caption className="sr-only">{t.channelsTitle}</caption><thead><tr><th scope="col">{t.channel}</th><th scope="col">{t.visits}</th><th scope="col">{t.conversions}</th><th scope="col">{t.conversion}</th></tr></thead><tbody>{data.channels.map(group => <tr key={group.channel}><th scope="row">{group.channel}</th><td>{f.number(group.visits)}</td><td>{f.number(group.convertedVisits)}</td><td>{f.percent(group.conversion)}</td></tr>)}</tbody></table>}
  >
    <BarChart data={data.channels} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
      <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
      <XAxis tickFormatter={(value: string) => value.length > 14 ? `${value.slice(0, 13)}…` : value} dataKey="channel" minTickGap={16} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <YAxis domain={[0, "auto"]} width={52} allowDecimals={false} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <ChartTooltip formatter={value => f.number(typeof value === "number" ? value : null)} />
      <Bar dataKey={metric} name={label} fill="var(--primary)" radius={[4, 4, 0, 0]} maxBarSize={56} isAnimationActive={false} />
    </BarChart>
  </ChartContainer>;
}
03Product analytics

Visit conversion

Daily conversion with missing values distinct from zero.

@neumorphism-ui/chart-conversion
Usage
Usage
"use client";
import { ConversionChart } from "@/components/blocks/conversion-chart";
import { summarizeAnalytics, type AnalyticsRecord } from "@/lib/analytics-model";

export function Example({ records }: { records: AnalyticsRecord[] }) {
  return <ConversionChart data={summarizeAnalytics(records, 14)} locale="en" />;
}
Source
@components/blocks/conversion-chart.tsx
"use client";

import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import type { AnalyticsSummary } from "@/lib/analytics-model";
import { analyticsCopy, analyticsFormats, type AnalyticsLocale } from "@/lib/analytics-copy";

export function ConversionChart({ data, locale = "en" }: { data: AnalyticsSummary; locale?: AnalyticsLocale }) {
  const t = analyticsCopy[locale];
  const f = analyticsFormats(locale);
  return <ChartContainer
    data-chart="conversion" title={t.conversionTitle} description={t.conversionBody}
    summary={data.observedDays ? <>{f.percent(data.conversion)} · {f.number(data.totals.convertedVisits)} / {f.number(data.totals.visits)}</> : t.empty}
    tableLabel={t.table} empty={!data.observedDays} emptyLabel={t.empty}
    table={<table><caption className="sr-only">{t.conversionTitle}</caption><thead><tr><th scope="col">{t.date}</th><th scope="col">{t.visits}</th><th scope="col">{t.conversions}</th><th scope="col">{t.conversion}</th></tr></thead><tbody>{data.points.map(point => <tr key={point.date}><th scope="row">{point.date}</th><td>{f.number(point.visits)}</td><td>{f.number(point.convertedVisits)}</td><td>{f.percent(point.conversion)}</td></tr>)}</tbody></table>}
  >
    <LineChart data={data.points} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
      <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
      <XAxis dataKey="date" tickFormatter={(value: string) => value.slice(5)} minTickGap={28} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <YAxis domain={[0, 1]} ticks={[0, 0.25, 0.5, 0.75, 1]} width={48} tickFormatter={(value: number) => `${value * 100}%`} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
      <ChartTooltip formatter={value => f.percent(typeof value === "number" ? value : null)} />
      <Line type="linear" dataKey="conversion" name={t.conversion} stroke="var(--primary)" strokeWidth={2.5} dot={{ r: 2 }} activeDot={{ r: 5 }} connectNulls={false} isAnimationActive={false} />
    </LineChart>
  </ChartContainer>;
}
04Operations & development

Build duration

Cold and cached build duration with an explicit budget.

@neumorphism-ui/chart-build-duration
Usage
Usage
"use client";
import { BuildDurationChart, type BuildDurationPoint } from "@/components/blocks/build-duration-chart";

const data: BuildDurationPoint[] = [
  { label: "B1", coldSeconds: 210, cachedSeconds: 108 },
];

export function Example() {
  return <BuildDurationChart data={data} />;
}
Source
@components/blocks/build-duration-chart.tsx
"use client";

import * as React from "react";
import { CartesianGrid, Line, LineChart, ReferenceLine, XAxis, YAxis } from "recharts";

import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Select, SelectItem } from "@/components/ui/select";

export type BuildDurationPoint = {
  label: string;
  coldSeconds: number;
  cachedSeconds: number;
};

export function BuildDurationChart({
  data,
  budgetSeconds = 220,
}: {
  data: readonly BuildDurationPoint[];
  budgetSeconds?: number;
}) {
  const [series, setSeries] = React.useState<"both" | "cold" | "cached">("both");
  const coldMean = data.length ? data.reduce((sum, row) => sum + row.coldSeconds, 0) / data.length : 0;
  const cachedMean = data.length ? data.reduce((sum, row) => sum + row.cachedSeconds, 0) / data.length : 0;
  const breaches = data.filter((row) => row.coldSeconds > budgetSeconds).length;

  return (
    <ChartContainer
      data-chart="build-duration"
      title="Build duration"
      description="Paired cold and cached build runs in seconds."
      summary={data.length ? `Mean cold ${coldMean.toFixed(1)} s · cached ${cachedMean.toFixed(1)} s · ${breaches}/${data.length} cold runs over budget` : "No build runs."}
      actions={
        <label className="grid gap-1.5 text-xs">
          Visible series
          <Select value={series} onChange={(event) => setSeries(event.target.value as typeof series)}>
            <SelectItem value="both">Both</SelectItem>
            <SelectItem value="cold">Cold only</SelectItem>
            <SelectItem value="cached">Cached only</SelectItem>
          </Select>
        </label>
      }
      tableLabel="View build data"
      empty={!data.length}
      table={
        <table>
          <caption className="sr-only">Build duration data</caption>
          <thead><tr><th>Build</th><th>Cold (s)</th><th>Cached (s)</th><th>Saved (s)</th></tr></thead>
          <tbody>{data.map((row) => <tr key={row.label}><th scope="row">{row.label}</th><td>{row.coldSeconds}</td><td>{row.cachedSeconds}</td><td>{row.coldSeconds - row.cachedSeconds}</td></tr>)}</tbody>
        </table>
      }
    >
      <LineChart data={data} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
        <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
        <XAxis dataKey="label" tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <YAxis domain={[0, "auto"]} width={52} tickFormatter={(value: number) => `${value}s`} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <ReferenceLine y={budgetSeconds} stroke="var(--destructive)" strokeDasharray="4 4" />
        <ChartTooltip formatter={(value) => `${Number(value)} s`} />
        {series !== "cached" && <Line dataKey="coldSeconds" name="Cold build" stroke="var(--foreground)" strokeWidth={2.5} dot={{ r: 3 }} isAnimationActive={false} />}
        {series !== "cold" && <Line dataKey="cachedSeconds" name="Cached build" stroke="var(--primary)" strokeWidth={2.5} strokeDasharray="7 4" dot={{ r: 3 }} isAnimationActive={false} />}
      </LineChart>
    </ChartContainer>
  );
}

const exampleBuilds: BuildDurationPoint[] = [
  { label: "B1", coldSeconds: 210, cachedSeconds: 108 },
  { label: "B2", coldSeconds: 195, cachedSeconds: 102 },
  { label: "B3", coldSeconds: 238, cachedSeconds: 114 },
  { label: "B4", coldSeconds: 226, cachedSeconds: 109 },
  { label: "B5", coldSeconds: 204, cachedSeconds: 96 },
  { label: "B6", coldSeconds: 188, cachedSeconds: 93 },
];

export function BuildDurationExample() {
  return <BuildDurationChart data={exampleBuilds} />;
}
05Operations & development

Service latency

p50 and p95 latency against a visible p95 budget.

@neumorphism-ui/chart-service-latency
Usage
Usage
"use client";
import { ServiceLatencyChart, type ServiceLatencyPoint } from "@/components/blocks/service-latency-chart";

const data: ServiceLatencyPoint[] = [
  { label: "Mon", p50Ms: 80, p95Ms: 210 },
];

export function Example() {
  return <ServiceLatencyChart data={data} />;
}
Source
@components/blocks/service-latency-chart.tsx
"use client";

import { CartesianGrid, Line, LineChart, ReferenceLine, XAxis, YAxis } from "recharts";

import { ChartContainer, ChartTooltip } from "@/components/ui/chart";

export type ServiceLatencyPoint = {
  label: string;
  p50Ms: number;
  p95Ms: number;
};

export function ServiceLatencyChart({
  data,
  budgetMs = 300,
}: {
  data: readonly ServiceLatencyPoint[];
  budgetMs?: number;
}) {
  const worst = data.length ? Math.max(...data.map((row) => row.p95Ms)) : 0;
  const breaches = data.filter((row) => row.p95Ms > budgetMs);

  return (
    <ChartContainer
      data-chart="service-latency"
      title="Service latency"
      description="Daily p50 and p95 response time against a p95 budget."
      summary={data.length ? `Worst p95 ${worst} ms · ${breaches.length}/${data.length} periods over budget` : "No latency samples."}
      tableLabel="View latency data"
      empty={!data.length}
      table={
        <table>
          <caption className="sr-only">Service latency data</caption>
          <thead><tr><th>Period</th><th>p50 (ms)</th><th>p95 (ms)</th><th>Budget</th></tr></thead>
          <tbody>{data.map((row) => <tr key={row.label}><th scope="row">{row.label}</th><td>{row.p50Ms}</td><td>{row.p95Ms}</td><td>{row.p95Ms > budgetMs ? "Over" : "Within"}</td></tr>)}</tbody>
        </table>
      }
    >
      <LineChart data={data} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
        <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
        <XAxis dataKey="label" tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <YAxis domain={[0, "auto"]} width={52} tickFormatter={(value: number) => `${value}ms`} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <ReferenceLine y={budgetMs} stroke="var(--destructive)" strokeDasharray="4 4" />
        <ChartTooltip formatter={(value) => `${Number(value)} ms`} />
        <Line dataKey="p50Ms" name="p50" stroke="var(--muted-foreground)" strokeWidth={2} strokeDasharray="7 4" dot={false} isAnimationActive={false} />
        <Line dataKey="p95Ms" name="p95" stroke="var(--primary)" strokeWidth={2.5} dot={{ r: 3 }} isAnimationActive={false} />
      </LineChart>
    </ChartContainer>
  );
}

const exampleLatency: ServiceLatencyPoint[] = [
  { label: "Mon", p50Ms: 80, p95Ms: 210 },
  { label: "Tue", p50Ms: 76, p95Ms: 195 },
  { label: "Wed", p50Ms: 95, p95Ms: 340 },
  { label: "Thu", p50Ms: 130, p95Ms: 470 },
  { label: "Fri", p50Ms: 100, p95Ms: 330 },
  { label: "Sat", p50Ms: 82, p95Ms: 240 },
  { label: "Sun", p50Ms: 79, p95Ms: 215 },
];

export function ServiceLatencyExample() {
  return <ServiceLatencyChart data={exampleLatency} />;
}
06Operations & development

Release activity

New installs and updates in counts or per-release share.

@neumorphism-ui/chart-release-activity
Usage
Usage
"use client";
import { ReleaseActivityChart, type ReleaseActivityPoint } from "@/components/blocks/release-activity-chart";

const data: ReleaseActivityPoint[] = [
  { release: "R1", installs: 148, updates: 62 },
];

export function Example() {
  return <ReleaseActivityChart data={data} />;
}
Source
@components/blocks/release-activity-chart.tsx
"use client";

import * as React from "react";
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";

import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Select, SelectItem } from "@/components/ui/select";

export type ReleaseActivityPoint = {
  release: string;
  installs: number;
  updates: number;
};

export function ReleaseActivityChart({
  data,
}: {
  data: readonly ReleaseActivityPoint[];
}) {
  const [view, setView] = React.useState<"count" | "share">("count");
  const installs = data.reduce((sum, row) => sum + row.installs, 0);
  const updates = data.reduce((sum, row) => sum + row.updates, 0);
  const total = installs + updates;

  return (
    <ChartContainer
      data-chart="release-activity"
      title="Release activity"
      description="New installs and updates by release. Counts are events, not unique users."
      summary={data.length ? `Total activity ${total.toLocaleString()} · update share ${total ? ((updates / total) * 100).toFixed(1) : "0.0"}%` : "No release activity."}
      actions={
        <label className="grid gap-1.5 text-xs">
          View
          <Select value={view} onChange={(event) => setView(event.target.value as typeof view)}>
            <SelectItem value="count">Event counts</SelectItem>
            <SelectItem value="share">Share per release</SelectItem>
          </Select>
        </label>
      }
      tableLabel="View release data"
      empty={!data.length}
      table={
        <table>
          <caption className="sr-only">Release activity data</caption>
          <thead><tr><th>Release</th><th>Installs</th><th>Updates</th><th>Update share</th></tr></thead>
          <tbody>{data.map((row) => {
            const rowTotal = row.installs + row.updates;
            return <tr key={row.release}><th scope="row">{row.release}</th><td>{row.installs}</td><td>{row.updates}</td><td>{rowTotal ? ((row.updates / rowTotal) * 100).toFixed(1) + "%" : "—"}</td></tr>;
          })}</tbody>
        </table>
      }
    >
      <AreaChart data={data} stackOffset={view === "share" ? "expand" : "none"} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
        <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
        <XAxis dataKey="release" tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <YAxis domain={view === "share" ? [0, 1] : [0, "auto"]} width={52} tickFormatter={(value: number) => view === "share" ? `${Math.round(value * 100)}%` : String(value)} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <ChartTooltip formatter={(value) => Number(value).toLocaleString()} />
        <Area dataKey="installs" name="New installs" stackId="activity" stroke="var(--foreground)" fill="var(--primary)" fillOpacity={0.22} strokeWidth={2} isAnimationActive={false} />
        <Area dataKey="updates" name="Updates" stackId="activity" stroke="var(--muted-foreground)" fill="var(--muted)" fillOpacity={0.8} strokeWidth={2} strokeDasharray="6 4" isAnimationActive={false} />
      </AreaChart>
    </ChartContainer>
  );
}

const exampleActivity: ReleaseActivityPoint[] = [
  { release: "R1", installs: 148, updates: 62 },
  { release: "R2", installs: 232, updates: 104 },
  { release: "R3", installs: 196, updates: 88 },
  { release: "R4", installs: 284, updates: 132 },
  { release: "R5", installs: 341, updates: 176 },
  { release: "R6", installs: 378, updates: 209 },
];

export function ReleaseActivityExample() {
  return <ReleaseActivityChart data={exampleActivity} />;
}
07Operations & development

Delivery capacity

Planned versus delivered items and signed variance.

@neumorphism-ui/chart-delivery-capacity
Usage
Usage
"use client";
import { DeliveryCapacityChart, type DeliveryCapacityPoint } from "@/components/blocks/delivery-capacity-chart";

const data: DeliveryCapacityPoint[] = [
  { period: "S1", planned: 32, delivered: 28 },
];

export function Example() {
  return <DeliveryCapacityChart data={data} />;
}
Source
@components/blocks/delivery-capacity-chart.tsx
"use client";

import * as React from "react";
import { Bar, BarChart, CartesianGrid, ReferenceLine, XAxis, YAxis } from "recharts";

import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Select, SelectItem } from "@/components/ui/select";

export type DeliveryCapacityPoint = {
  period: string;
  planned: number;
  delivered: number;
};

export function DeliveryCapacityChart({
  data,
}: {
  data: readonly DeliveryCapacityPoint[];
}) {
  const [view, setView] = React.useState<"compare" | "variance">("compare");
  const planned = data.reduce((sum, row) => sum + row.planned, 0);
  const delivered = data.reduce((sum, row) => sum + row.delivered, 0);
  const chartData = data.map((row) => ({ ...row, variance: row.delivered - row.planned }));

  return (
    <ChartContainer
      data-chart="delivery-capacity"
      title="Delivery capacity"
      description="Planned and delivered item counts. This is not a productivity score."
      summary={data.length ? `Planned ${planned} · delivered ${delivered} · net variance ${delivered - planned >= 0 ? "+" : ""}${delivered - planned}` : "No delivery records."}
      actions={
        <label className="grid gap-1.5 text-xs">
          View
          <Select value={view} onChange={(event) => setView(event.target.value as typeof view)}>
            <SelectItem value="compare">Planned vs delivered</SelectItem>
            <SelectItem value="variance">Signed variance</SelectItem>
          </Select>
        </label>
      }
      tableLabel="View delivery data"
      empty={!data.length}
      table={
        <table>
          <caption className="sr-only">Delivery capacity data</caption>
          <thead><tr><th>Period</th><th>Planned</th><th>Delivered</th><th>Variance</th></tr></thead>
          <tbody>{chartData.map((row) => <tr key={row.period}><th scope="row">{row.period}</th><td>{row.planned}</td><td>{row.delivered}</td><td>{row.variance > 0 ? "+" + row.variance : row.variance}</td></tr>)}</tbody>
        </table>
      }
    >
      <BarChart data={chartData} accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
        <CartesianGrid vertical={false} stroke="var(--border)" strokeDasharray="3 4" />
        <XAxis dataKey="period" tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <YAxis width={44} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <ReferenceLine y={0} stroke="var(--foreground)" />
        <ChartTooltip />
        {view === "variance" ? (
          <Bar dataKey="variance" name="Delivered − planned" fill="var(--primary)" maxBarSize={52} isAnimationActive={false} />
        ) : (
          <>
            <Bar dataKey="planned" name="Planned" fill="var(--muted)" stroke="var(--muted-foreground)" strokeDasharray="4 3" maxBarSize={40} isAnimationActive={false} />
            <Bar dataKey="delivered" name="Delivered" fill="var(--primary)" maxBarSize={40} isAnimationActive={false} />
          </>
        )}
      </BarChart>
    </ChartContainer>
  );
}

const exampleCapacity: DeliveryCapacityPoint[] = [
  { period: "S1", planned: 32, delivered: 28 },
  { period: "S2", planned: 36, delivered: 39 },
  { period: "S3", planned: 34, delivered: 31 },
  { period: "S4", planned: 38, delivered: 42 },
];

export function DeliveryCapacityExample() {
  return <DeliveryCapacityChart data={exampleCapacity} />;
}
08Operations & development

Install diagnostics

Sequential install-stage timings with unit switching.

@neumorphism-ui/chart-install-diagnostics
Usage
Usage
"use client";
import { InstallDiagnosticsChart, type InstallDiagnosticPoint } from "@/components/blocks/install-diagnostics-chart";

const data: InstallDiagnosticPoint[] = [
  { stage: "Download", durationMs: 840 },
];

export function Example() {
  return <InstallDiagnosticsChart data={data} />;
}
Source
@components/blocks/install-diagnostics-chart.tsx
"use client";

import * as React from "react";
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";

import { ChartContainer, ChartTooltip } from "@/components/ui/chart";
import { Select, SelectItem } from "@/components/ui/select";

export type InstallDiagnosticPoint = {
  stage: string;
  durationMs: number;
};

export function InstallDiagnosticsChart({
  data,
}: {
  data: readonly InstallDiagnosticPoint[];
}) {
  const [unit, setUnit] = React.useState<"ms" | "s">("ms");
  const total = data.reduce((sum, row) => sum + row.durationMs, 0);
  const largest = data.length ? data.reduce((best, row) => row.durationMs > best.durationMs ? row : best) : null;
  const format = (value: number) => unit === "ms" ? `${value} ms` : `${(value / 1000).toFixed(2)} s`;

  return (
    <ChartContainer
      data-chart="install-diagnostics"
      title="Installation diagnostics"
      description="Sequential stage durations for one installation trace."
      summary={largest ? `Total ${format(total)} · longest stage ${largest.stage} (${format(largest.durationMs)})` : "No installation trace."}
      actions={
        <label className="grid gap-1.5 text-xs">
          Unit
          <Select value={unit} onChange={(event) => setUnit(event.target.value as typeof unit)}>
            <SelectItem value="ms">Milliseconds</SelectItem>
            <SelectItem value="s">Seconds</SelectItem>
          </Select>
        </label>
      }
      tableLabel="View installation data"
      empty={!data.length}
      table={
        <table>
          <caption className="sr-only">Installation diagnostic data</caption>
          <thead><tr><th>Stage</th><th>Duration</th><th>Share</th></tr></thead>
          <tbody>{data.map((row) => <tr key={row.stage}><th scope="row">{row.stage}</th><td>{format(row.durationMs)}</td><td>{total ? ((row.durationMs / total) * 100).toFixed(1) + "%" : "—"}</td></tr>)}</tbody>
        </table>
      }
    >
      <BarChart data={data} layout="vertical" accessibilityLayer margin={{ top: 12, right: 12, bottom: 4, left: 0 }}>
        <CartesianGrid horizontal={false} stroke="var(--border)" strokeDasharray="3 4" />
        <XAxis type="number" tickFormatter={(value: number) => format(value)} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <YAxis type="category" dataKey="stage" width={72} tick={{ fill: "var(--muted-foreground)" }} tickLine={false} axisLine={false} />
        <ChartTooltip formatter={(value) => format(Number(value))} />
        <Bar dataKey="durationMs" name="Duration" fill="var(--primary)" radius={[0, 4, 4, 0]} maxBarSize={42} isAnimationActive={false} />
      </BarChart>
    </ChartContainer>
  );
}

const exampleTrace: InstallDiagnosticPoint[] = [
  { stage: "Resolve", durationMs: 120 },
  { stage: "Download", durationMs: 840 },
  { stage: "Transform", durationMs: 210 },
  { stage: "Write", durationMs: 90 },
];

export function InstallDiagnosticsExample() {
  return <InstallDiagnosticsChart data={exampleTrace} />;
}
Neumorphism UI56 UI · 16 blocks · 80 registry items

shadcn-compatible source Registry

GitHubCredits