chartBarHorizontal
A Charts block/component from shadcn/ui — clean-room reimplemented for Domphy (see methodology). Call chartBarHorizontal() with no arguments for a working demo, or edit the code below live.
Implementation notes
Same monthly dataset rotated into horizontal bars against a left category axis, hidden value axis. The engine's built-in axis-trigger tooltip resolves hover position by comparing mouse X against the X scale unconditionally (packages/chart/src/engine.ts bindTooltipEvents) — meaningless for horizontal bars where the category axis is Y — and there is no item-trigger hit-testing for bar series at all, so the native tooltip is disabled (tooltip: {show:false}) and replaced with a custom mouse-to-row overlay (chartBarHorizontalHoverOverlay in chart-bar-shared.ts) built with @domphy/chart's own exported scale factories (createOrdinalScale/createLinearScale) against an explicit fixed-pixel grid, so it lines up exactly with the real bars. Category (y) axes render bottom-to-top in this engine, so the data/category arrays are reversed before rendering to keep the on-screen top-to-bottom order chronological (Jan at top). Corner radius again capped at the engine's hardcoded 2px regardless of the requested [0,5,5,0] radius.
Status: ported · Reference: shadcn/ui original
// shadcn/ui "chart-bar" (horizontal recipe) — clean-room reimplementation.
//
// The same single-series monthly dataset as the default recipe, rotated so
// each month is a row with a bar extending rightward from a left category
// axis; the numeric axis is fully hidden.
//
// Implemented purely from the block's public functional/visual spec — no
// upstream shadcn/ui source was viewed or copied.
import type { DomphyElement } from "@domphy/core";
import type { ChartOption } from "@domphy/chart";
import type { ThemeColor } from "@domphy/theme";
import {
CHART_BAR_MONTHLY_DATA,
chartBarCardShell,
chartBarCategoryYAxis,
chartBarFrame,
chartBarHiddenValueXAxis,
chartBarHorizontalHoverOverlay,
chartBarTrendFooter,
chartBarValueDomain,
type ChartBarGrid,
type ChartBarPoint,
type ChartTrendDirection,
} from "./chart-bar-shared.js";
export interface ChartBarHorizontalProps {
data?: ChartBarPoint[];
seriesLabel?: string;
seriesColor?: ThemeColor;
title?: string;
subtitle?: string;
trendText?: string;
trendDirection?: ChartTrendDirection;
captionText?: string;
/** Max characters kept from each category label before truncation. */
categoryTruncateLength?: number;
grid?: ChartBarGrid;
height?: number;
}
const DEFAULT_GRID: ChartBarGrid = { left: 44, right: 16, top: 8, bottom: 8 };
/**
* shadcn/ui "chart-bar" horizontal recipe — the default recipe's dataset
* rotated into rightward-extending rows against a left category axis. Call
* with no arguments for a working demo.
*/
function chartBarHorizontal(props: ChartBarHorizontalProps = {}): DomphyElement<"div"> {
const {
data = CHART_BAR_MONTHLY_DATA,
seriesLabel = "Desktop",
seriesColor = "primary",
title = "Bar Chart - Horizontal",
subtitle = "January - June 2026",
trendText = "Trending up by 5.2% this month",
trendDirection = "up",
captionText = "Showing total visitors for the last 6 months",
categoryTruncateLength = 12,
grid = DEFAULT_GRID,
height = 64,
} = props;
// Category (y) axes render bottom-to-top — reverse the data order so the
// on-screen reading order (top-to-bottom) stays chronological. See the
// file-level fidelity note in chart-bar-shared.ts.
const orderedData = [...data].reverse();
const categories = orderedData.map((point) => point.label.slice(0, categoryTruncateLength));
const values = orderedData.map((point) => point.value);
const [, domainMax] = chartBarValueDomain(values);
const option: ChartOption = {
tooltip: { show: false },
xAxis: chartBarHiddenValueXAxis({ min: 0, max: domainMax }),
yAxis: chartBarCategoryYAxis(categories),
grid,
series: [
{
type: "bar",
name: seriesLabel,
color: seriesColor,
itemStyle: { borderRadius: [0, 5, 5, 0] },
data: values,
},
],
};
return chartBarCardShell({
title,
subtitle,
content: {
div: [
chartBarFrame(option, {
height,
overlays: [
chartBarHorizontalHoverOverlay({
categories,
grid,
valueLabel: (index) => String(values[index] ?? ""),
}),
],
}),
],
},
footer: chartBarTrendFooter({ trendText, direction: trendDirection, captionText }),
});
}
export { chartBarHorizontal };