radial-chart-06
Half-circle storage gauge in warm paper and ink tones on a paper mat, ink on cream in light mode and cream on a dark mat in dark mode, with used and available capacity in text below.
pnpm dlx shadcn add @01orbital/radial-chart-06"use client"
import {
Label,
PolarAngleAxis,
PolarRadiusAxis,
RadialBar,
RadialBarChart,
} from "recharts"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { ChartContainer, type ChartConfig } from "@/components/ui/chart"
const COPY = {
title: "Storage",
description: "Workspace capacity",
used: "Used",
available: "Available",
} as const
// Capacity and usage are in GB; the axis uses the capacity as its full scale.
const STORAGE = { used: 68.4, capacity: 100 }
const AVAILABLE = Number((STORAGE.capacity - STORAGE.used).toFixed(1))
const PERCENT = Math.round((STORAGE.used / STORAGE.capacity) * 100)
const DATA = [{ used: STORAGE.used, fill: "var(--color-used)" }]
const CHART_CONFIG = {
used: { label: COPY.used, color: "var(--chart-2)" },
} satisfies ChartConfig
/** A storage gauge printed in warm paper ink on a paper mat: ink on cream in light mode, cream ink on a dark mat in dark mode. */
export default function RadialChart06() {
return (
// The paper mat and its ink swap by mode: dark ink on cream, then cream
// ink on a dark mat. No forced theme, so the page mode decides.
<div className="mx-auto w-full max-w-sm rounded-lg bg-[oklch(0.911_0.026_87)] p-[clamp(0rem,calc((100vw_-_20rem)/4),2rem)] text-foreground [--card-foreground:var(--foreground)] [--chart-2:var(--foreground)] [--paper-line:oklch(0.3_0.021_83/0.2)] [--primary:var(--foreground)] dark:bg-[oklch(0.16_0.008_89)] dark:[--card:oklch(0.192_0.01_89)] dark:[--foreground:oklch(0.841_0.011_95)] dark:[--muted-foreground:oklch(0.841_0.011_95/0.75)] dark:[--muted:oklch(0.28_0.01_89)] dark:[--paper-line:oklch(0.841_0.011_95/0.22)]">
<Card className="@container/storage mx-auto w-full max-w-xs rounded-none border border-(--paper-line) shadow-[inset_0_1px_0_oklch(1_0_0/0.8),0_2px_4px_oklch(0.3_0.021_83/0.1),0_22px_40px_-18px_oklch(0.3_0.021_83/0.4)] ring-0 dark:shadow-[inset_0_1px_0_oklch(0.841_0.011_95/0.18),0_30px_60px_-24px_oklch(0_0_0/0.7)] dark:ring-0">
<CardHeader>
<CardTitle>
<h2>{COPY.title}</h2>
</CardTitle>
<CardDescription>{COPY.description}</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer
config={CHART_CONFIG}
role="img"
aria-label={`${STORAGE.used} of ${STORAGE.capacity} GB used; ${AVAILABLE} GB available`}
className="mx-auto aspect-3/2 w-full max-w-72"
>
<RadialBarChart
accessibilityLayer={false}
data={DATA}
startAngle={180}
endAngle={0}
cy="85%"
// The enlarged radii fill a shallow frame rather than a full square.
innerRadius="115%"
outerRadius="150%"
margin={{ top: 6, right: 6, bottom: 6, left: 6 }}
>
<PolarAngleAxis
type="number"
domain={[0, STORAGE.capacity]}
tick={false}
/>
<RadialBar dataKey="used" background cornerRadius={10} />
<PolarRadiusAxis tick={false} tickLine={false} axisLine={false}>
<Label
content={({ viewBox }) => {
if (!viewBox || !("cx" in viewBox)) return null
return (
<text x={viewBox.cx} y={viewBox.cy} textAnchor="middle">
<tspan
x={viewBox.cx}
y={(viewBox.cy ?? 0) - 16}
fontSize={36}
className="fill-foreground font-medium tabular-nums"
>
{PERCENT}%
</tspan>
<tspan
x={viewBox.cx}
y={(viewBox.cy ?? 0) + 8}
fontSize={12}
className="fill-muted-foreground"
>
{COPY.used.toLowerCase()}
</tspan>
</text>
)
}}
/>
</PolarRadiusAxis>
</RadialBarChart>
</ChartContainer>
</CardContent>
<CardFooter>
<dl className="grid w-full grid-cols-1 gap-4 text-sm @[18rem]/storage:grid-cols-2">
<div className="space-y-1">
<dt className="text-muted-foreground">{COPY.used}</dt>
<dd className="font-medium tabular-nums">
{STORAGE.used} / {STORAGE.capacity} GB
</dd>
</div>
<div className="space-y-1 @[18rem]/storage:text-right">
<dt className="text-muted-foreground">{COPY.available}</dt>
<dd className="font-medium tabular-nums">{AVAILABLE} GB</dd>
</div>
</dl>
</CardFooter>
</Card>
</div>
)
}
Dependencies
npm: recharts@3.8.0. The CLI installs them for you.
Registry pieces it installs with it: