Skip to content
Domphy

chartBarLabelCustom

A Charts block/component from shadcn/ui — clean-room reimplemented for Domphy (see methodology). Call chartBarLabelCustom() with no arguments for a working demo, or edit the code below live.

Props

PropTypeDescription
dataChartBarTwoSeriesPoint[]
seriesLabelstring
seriesColorThemeColor
titlestring
subtitlestring
trendTextstring
trendDirectionChartTrendDirection
captionTextstring
gridChartBarGrid
heightnumber
Implementation notes

Horizontal single-series chart (desktop values from the two-series monthly dataset) with both axes fully hidden. Inside (category name, light on-fill color) and outside (value, foreground color) labels are drawn by a dedicated SVG overlay (chartBarInsideOutsideLabelOverlay) that replicates BarRenderer's own single-series horizontal bar-rect formula (bandwidth*0.65 thickness, band-centered) using the engine's exported scale factories — necessary because overlay/labels.ts's renderBarLabels only positions labels correctly for vertical bars (it always computes label position from xScale.bandwidth(), which is 0 for a value-type x-axis), so the built-in label option cannot be used for this orientation. A companion hover overlay still provides full-detail tooltips on demand.

Status: ported · Reference: shadcn/ui original

// shadcn/ui "chart-bar" (custom label recipe) — clean-room reimplementation.
//
// A horizontal single-series bar chart with both axes fully hidden: each
// bar prints its own category name near its inside-left edge (in a light,
// on-fill-legible color) and its numeric value just past its right end (in
// the normal foreground color), so the chart is self-describing without
// visible axis chrome.
//
// Implemented purely from the block's public functional/visual spec — no
// upstream shadcn/ui source was viewed or copied.

import type { ChartOption } from "@domphy/chart";
import type { DomphyElement } from "@domphy/core";
import {
  CHART_BAR_TWO_SERIES_DATA,
  type ChartBarGrid,
  type ChartBarTwoSeriesPoint,
  type ChartTrendDirection,
  chartBarCardShell,
  chartBarFrame,
  chartBarHorizontalHoverOverlay,
  chartBarInsideOutsideLabelOverlay,
  chartBarSeriesColor,
  chartBarTrendFooter,
  chartBarValueDomain,
} from "./chart-bar-shared.js";

export interface ChartBarLabelCustomProps {
  data?: ChartBarTwoSeriesPoint[];
  seriesLabel?: string;
  /** Bar color — a theme role (resolved at shift-9) or literal ramp hex.
   * Defaults to the ramp's second step (upstream var(--chart-2) ≈ blue-500). */
  seriesColor?: string;
  title?: string;
  subtitle?: string;
  trendText?: string;
  trendDirection?: ChartTrendDirection;
  captionText?: string;
  grid?: ChartBarGrid;
  height?: number;
}

const DEFAULT_GRID: ChartBarGrid = { left: 8, right: 48, top: 8, bottom: 8 };

// Upstream prints the FULL month name inside each bar — its LabelList
// (dataKey="month") has no formatter; only the hidden YAxis abbreviates via
// slice(0,3). The shared demo dataset stores abbreviated labels, so expand
// them for display here; any custom label that isn't a month abbreviation
// falls through unchanged.
const MONTH_NAMES: Record<string, string> = {
  Jan: "January",
  Feb: "February",
  Mar: "March",
  Apr: "April",
  May: "May",
  Jun: "June",
  Jul: "July",
  Aug: "August",
  Sep: "September",
  Oct: "October",
  Nov: "November",
  Dec: "December",
};

/**
 * shadcn/ui "chart-bar" custom-label recipe — inside category label +
 * outside value label, no visible axes. Call with no arguments for a
 * working demo.
 */
function chartBarLabelCustom(
  props: ChartBarLabelCustomProps = {},
): DomphyElement<"div"> {
  const {
    data = CHART_BAR_TWO_SERIES_DATA,
    seriesLabel = "Desktop",
    seriesColor = chartBarSeriesColor(1).css,
    title = "Bar Chart - Custom Label",
    subtitle = "January - June 2026",
    trendText = "Trending up by 5.2% this month",
    trendDirection = "up",
    captionText = "Showing total visitors for the last 6 months",
    grid = DEFAULT_GRID,
    height = 64,
  } = props;

  // Category (y) axes render bottom-to-top — reverse so the on-screen
  // reading order stays chronological.
  const orderedData = [...data].reverse();
  const categories = orderedData.map(
    (point) => MONTH_NAMES[point.label] ?? point.label,
  );
  const values = orderedData.map((point) => point.desktop);
  const valueDomain = chartBarValueDomain(values);

  const option: ChartOption = {
    tooltip: { show: false },
    xAxis: {
      type: "value",
      min: valueDomain[0],
      max: valueDomain[1],
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { show: false },
      splitLine: { show: true },
    },
    yAxis: {
      type: "category",
      data: categories,
      show: false,
    },
    grid,
    series: [
      {
        type: "bar",
        name: seriesLabel,
        color: seriesColor,
        data: values,
      },
    ],
  };

  return chartBarCardShell({
    title,
    subtitle,
    content: {
      div: [
        chartBarFrame(option, {
          height,
          overlays: [
            chartBarInsideOutsideLabelOverlay({
              categories,
              values,
              valueDomain,
              grid,
              // Upstream fills the inside label with var(--background) (a
              // neutral, near-background color) — not the series hue. Neutral
              // shift-1 is the closest role match the shared overlay exposes.
              insideColor: "neutral",
              insideLabel: (index) => categories[index] ?? "",
              outsideLabel: (index) => String(values[index] ?? ""),
            }),
            chartBarHorizontalHoverOverlay({
              categories,
              grid,
              showCategoryTitle: true,
              // Match upstream ChartTooltipContent indicator="line": a thin
              // line swatch in the series color + the series name + the value.
              valueLabel: (index) => String(values[index] ?? ""),
            }),
          ],
        }),
      ],
    },
    footer: chartBarTrendFooter({
      trendText,
      direction: trendDirection,
      captionText,
    }),
  });
}

export { chartBarLabelCustom };

← Back to shadcn/ui catalog