# Cache tiers echarts radial chart

> Semi-circle multi-ring gauge with a stat grid and a share legend

A shadcn/ui block from the [@evilcharts](https://evilcharts.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/evilcharts/cache-tiers-echarts-radial-chart).

- Page, with a live preview: https://shadcn.exchange/r/evilcharts/cache-tiers-echarts-radial-chart
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/evilcharts/cache-tiers-echarts-radial-chart-light-16f18a50ba9bf54f.png
- Registry item: https://evilcharts.com/r/cache-tiers-echarts-radial-chart.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://evilcharts.com/r/cache-tiers-echarts-radial-chart.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `echarts`, `motion`
- Registry items: `@evilcharts/echarts-radial-chart`

## Files

### `src/registry/blocks/echarts/b-cache-tiers-echarts-radial-chart.tsx`

```tsx
"use client";

import { EChartsRadialChart, type ChartConfig } from "@/registry/charts/echarts-radial-chart";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";

const TOTAL = 1000;

const TIERS = [
  {
    name: "memory",
    label: "L1 Memory",
    count: 610,
    swatch: "bg-[#dc2626] dark:bg-[#ef4444]",
  },
  {
    name: "regional",
    label: "L2 Regional",
    count: 240,
    swatch: "bg-[#d97706] dark:bg-[#f59e0b]",
  },
  {
    name: "overflow",
    label: "Edge Overflow",
    count: 100,
    swatch: "bg-[#2563eb] dark:bg-[#3b82f6]",
  },
  {
    name: "origin",
    label: "Origin Fetch",
    count: 50,
    swatch: "bg-[#0f172a] dark:bg-white",
  },
];

const STATS = [
  { name: "warm", label: "Served Warm", value: 9150 },
  { name: "revalidated", label: "Revalidated", value: 1280 },
  { name: "evictions", label: "Evictions", value: 412 },
  { name: "purges", label: "Purges", value: 96 },
];

const chartConfig = {
  memory: { label: "L1 Memory", colors: { light: ["#dc2626"], dark: ["#ef4444"] } },
  regional: { label: "L2 Regional", colors: { light: ["#d97706"], dark: ["#f59e0b"] } },
  overflow: { label: "Edge Overflow", colors: { light: ["#2563eb"], dark: ["#3b82f6"] } },
  origin: { label: "Origin Fetch", colors: { light: ["#0f172a"], dark: ["#ffffff"] } },
} satisfies ChartConfig;

const chartData = [...TIERS]
  .reverse()
  .map(({ name, count }) => ({ name, share: (count / TOTAL) * 100 }));

const count = (value: number) => value.toLocaleString("en-US");

function useCompactRings() {
  const [compact, setCompact] = useState(false);

  useEffect(() => {
    const query = window.matchMedia("(max-width: 639px)");
    const sync = () => setCompact(query.matches);
    sync();
    query.addEventListener("change", sync);
    return () => query.removeEventListener("change", sync);
  }, []);

  return compact;
}

export function EChartsCacheTiersRadialChart() {
  const compact = useCompactRings();

  return (
    <div className="flex h-full w-full flex-col gap-2 p-3 sm:gap-3 sm:p-4">
      <div className="flex min-h-0 flex-1 gap-3 sm:gap-4">
        <div className="relative min-h-0 flex-1 sm:-mb-10">
          <EChartsRadialChart
            data={chartData}
            config={chartConfig}
            nameKey="name"
            variant="semi"
            max={100}
            innerRadius="38%"
            outerRadius="96%"
            className="h-full w-full"
          >
            <EChartsRadialChart.RadialBar
              dataKey="share"
              barSize={compact ? 7 : 13}
              cornerRadius={compact ? 4 : 7}
            />
          </EChartsRadialChart>
        </div>

        <div className="grid shrink-0 grid-cols-2 content-center gap-x-4 gap-y-5 sm:w-[40%] sm:max-w-64">
          {STATS.map(({ name, label, value }) => (
            <div key={name} className="flex flex-col gap-1">
              <span className="text-muted-foreground truncate text-xs sm:text-sm">{label}</span>
              <span className="text-primary text-lg leading-none font-medium tabular-nums sm:text-xl">
                {count(value)}
              </span>
            </div>
          ))}
        </div>
      </div>

      <div className="grid shrink-0 grid-cols-2 gap-x-4 gap-y-2 border-t pt-2 sm:grid-cols-4 sm:gap-y-3 sm:pt-3">
        {TIERS.map(({ name, label, count: hits, swatch }) => (
          <div key={name} className="flex flex-col gap-1">
            <div className="flex items-center gap-1.5">
              <span className={cn("size-2.5 shrink-0 rounded-[3px]", swatch)} />
              <span className="text-primary truncate text-xs">{label}</span>
            </div>
            <span className="text-muted-foreground text-xs tabular-nums">
              {count(hits)}/{count(TOTAL)} ({Math.round((hits / TOTAL) * 100)}%)
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}
```
