Ridgeline

Overlapping distributions, one ridge per group: hover to lift a ridge and read its quartiles, switch datasets and every curve morphs.

datachartnew
Preview
Ridgeline preview

Daily highs in Zurich
SeriesMedianFirst quartileThird quartileLowestHighestCount
July24°C21.8°C26.3°C18°C30°C48
August27°C25°C29°C22°C32°C48
September21.5°C18°C24°C14°C28°C48

When to use

  • Seasonal or categorical distributions such as temperatures by month or response times by region.
  • Showing where two groups differ in shape, such as bimodal winters.

When not to use

  • Use beeswarm when each individual observation matters.
  • Use bar-chart when only one summary number per group matters.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ridgeline",
  "type": "registry:ui",
  "title": "Ridgeline",
  "description": "Overlapping distributions, one ridge per group: hover to lift a ridge and read its quartiles, switch datasets and every curve morphs.",
  "categories": [
    "data"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/ridgeline/ridgeline.tsx",
      "type": "registry:component",
      "target": "@components/manicat/ridgeline/ridgeline.tsx"
    },
    {
      "path": "registry/components/ridgeline/ridgeline.module.css",
      "type": "registry:component",
      "target": "@components/manicat/ridgeline/ridgeline.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/ridgeline",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/ridgeline",
    "markdown": "https://uiarc.dev/components/ridgeline/markdown",
    "tags": [
      "data",
      "chart",
      "new"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { Ridgeline } from "@/components/manicat/ridgeline/ridgeline";
import type { RidgelineSeries } from "@/components/manicat/ridgeline/ridgeline";

function ridge(mean: number, spread: number): number[] {
  return Array.from({ length: 48 }, (_, index) => mean + Math.round(spread * Math.sin(index * 1.7) + ((index * 7) % 5) - 2));
}

const ridgeSeries: RidgelineSeries[] = [
  { id: "jul", label: "July", values: ridge(24, 4) },
  { id: "aug", label: "August", values: ridge(27, 3) },
  { id: "sep", label: "September", values: ridge(21, 5) },
];

export function RidgelineExample() {
  return <Ridgeline series={ridgeSeries} label="Daily highs in Zurich" unit="°C" tint />;
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Temperature distributions Overlapping density curves compare how a value is spread across groups.

Temperature distributions Overlapping density curves compare how a value is spread across groups. preview

Daily highs in Zurich
SeriesMedianFirst quartileThird quartileLowestHighestCount
July24°C21.8°C26.3°C18°C30°C48
August27°C25°C29°C22°C32°C48
September21.5°C18°C24°C14°C28°C48

Keyboard interactions

KeysAction
ArrowUp / ArrowDownLifts the previous or next ridge.
ArrowLeft / ArrowRightMoves the reading cursor along the values by one axis step.
Home / EndLifts the first or last ridge.
EscapeLowers the ridge and clears the reading.

Accessibility

  • The plot is a focusable group; the lifted ridge's median, middle half, range, and count are announced through a polite live region.
  • A visually hidden table lists median, quartiles, extremes, and count for every series.
  • Every row is labelled in text; the median tint is backed by the table and tooltip.

Motion

  • Curves are sampled on a shared grid, so a new dataset or domain interpolates every point: ridges swell, slide, and split in one spring.
  • The first reveal rolls the ridges in from the top row down.
  • The lifted ridge is a copy drawn above the others that rises 6px on a spring, with its middle half shaded and its median marked.
  • The tooltip glides after the pointer and stays inside the chart. Reduced motion draws final shapes immediately.

Responsive behavior

  • Label gutter and tick count adapt to width; the plot redraws at the measured size so strokes stay crisp.
  • Touch taps lift a ridge and read at the tapped value.

Performance

  • Densities are computed once per dataset (96 samples per ridge); animation interpolates arrays and writes path strings directly.
  • Suitable for up to a few thousand observations per series.

Notes for AI

  • Choose it to compare the shape of many distributions: spread, skew, and bimodality, not just averages.
  • Pass raw observations, at least 20 per series, and fix domain when switching datasets so shapes move on a still axis.
  • Keep rows to about 20; beyond that, use small multiples or a box plot.
  • Colors come from the shared --series-1 to --series-4 tokens in foundation.css: series 1 is the accent itself and series 2 to 4 rotate its hue, validated all-pairs for color vision deficiency across every accent in light and dark. Override the tokens on any ancestor to rebrand.