Charts & Statschartsvgzero-dependencybarhorizontalrankingtop-list
Horizontal Bar Chart
Ranked horizontal bars with labels and values, single color and optional highlight of one item.
Live Preview
Unique visitors, last 30 days
- /pricing18.4k
- /docs/getting-started15.2k
- /13k
- /blog/astro-59.3k
- /components7.1k
- /changelog4.3k
View data
| Item | Value |
|---|---|
| /pricing | 18.4k |
| /docs/getting-started | 15.2k |
| / | 13k |
| /blog/astro-5 | 9.3k |
| /components | 7.1k |
| /changelog | 4.3k |
Usage
Copy and paste this code into your React component:
tsx
import { HorizontalBarChart } from 'react-component-kit-ui/charts';
<HorizontalBarChart items={[
{ label: '/pricing', value: 18420 },
{ label: '/docs', value: 15210 },
{ label: '/', value: 12980 },
]} highlight="/pricing" />Manual Installation
If you are not using the npm package, create a new file and paste the following code: src/components/HorizontalBarChart.tsx + HorizontalBarChart.css:
HorizontalBarChart.tsx
'use client';
import type { CSSProperties } from 'react';
import ChartFrame from './ChartFrame';
import { formatValue } from './chart-utils';
import './HorizontalBarChart.css';
/** "a: b; --c: d" โ style object */
function st(css: string | undefined): CSSProperties {
const o: Record<string, string> = {};
for (const decl of String(css ?? '').split(';')) {
const i = decl.indexOf(':');
if (i < 0) continue;
const k = decl.slice(0, i).trim();
if (k) o[k.startsWith('--') ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = decl.slice(i + 1).trim();
}
return o as CSSProperties;
}
/**
* HorizontalBarChart โ Ranked horizontal bars with labels on the left and values
* on the right. One series โ one color; optionally highlight a single bar.
*
* @param {Array<{label: string, value: number}>} items - Bars to draw.
* @param {boolean} sort - Sort descending by value.
* @param {string} highlight - Label to emphasise; other bars recede.
* @param {string} prefix - Value prefix.
* @param {string} suffix - Value suffix.
* @param {string} color - Bar color.
* @param {string} variant - Card style (glass|solid|ghost).
* @param {string} size - Density (sm|md|lg).
*/
export interface HorizontalBarChartProps {
title?: string;
subtitle?: string;
items?: Array<{ label: string; value: number }>;
sort?: boolean;
highlight?: string;
prefix?: string;
suffix?: string;
color?: string;
variant?: 'glass' | 'solid' | 'ghost';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
export function HorizontalBarChart({
title = 'Top pages',
subtitle = 'Unique visitors, last 30 days',
items = [
{ label: '/pricing', value: 18420 },
{ label: '/docs/getting-started', value: 15210 },
{ label: '/', value: 12980 },
{ label: '/blog/astro-5', value: 9340 },
{ label: '/components', value: 7120 },
{ label: '/changelog', value: 4310 },
],
sort = true,
highlight,
prefix = '',
suffix = '',
color = 'var(--chart-1, #3987e5)',
variant = 'glass',
size = 'md',
className = '',
}: HorizontalBarChartProps) {
const rows = sort ? [...items].sort((a, b) => b.value - a.value) : items;
const max = Math.max(...rows.map((r) => r.value)) || 1;
const fmt = (v: number) => formatValue(v, prefix, suffix);
return (
<>
<ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} table={{ headers: ['Item', 'Value'], rows: rows.map((r) => [r.label, fmt(r.value)]) }}>
<ol className="hb" aria-label={title} data-rk-344822="">
{rows.map((r, i) => {
const dim = highlight && highlight !== r.label;
return (
<li className="hb__row cf-mark" tabIndex={0} style={st(`--hb-w:${(r.value / max) * 100}%; --hb-c:${color}; --hb-o:${dim ? 0.35 : 1}; --hb-i:${i}`)} data-tip-title={r.label} data-tip={`${color}::${fmt(r.value)} ยท ${((r.value / max) * 100).toFixed(0)}% of top`} aria-label={`${i + 1}. ${r.label}: ${fmt(r.value)}`} data-rk-344822="">
<span className="hb__label" data-rk-344822="">{r.label}</span>
<span className="hb__track" aria-hidden="true" data-rk-344822=""><span className="hb__bar" data-rk-344822=""></span></span>
<span className="hb__value" data-rk-344822="">{fmt(r.value)}</span>
</li>
);
})}
</ol>
</ChartFrame>
</>
);
}
export default HorizontalBarChart;HorizontalBarChart.css
/* HorizontalBarChart โ ported from astro-component-kit (charts/HorizontalBarChart.astro) */
.hb:where([data-rk-344822]) {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.35rem;
}
.hb__row:where([data-rk-344822]) {
display: grid;
grid-template-columns: minmax(6rem, 34%) 1fr auto;
align-items: center;
gap: 0.75rem;
min-height: 32px;
padding: 0.15rem 0.35rem;
border-radius: 8px;
cursor: default;
}
.hb__row:hover:where([data-rk-344822]),
.hb__row:focus-visible:where([data-rk-344822]) {
background: rgb(var(--ink-rgb, 255 255 255) / 0.04);
}
.hb__row:focus-visible:where([data-rk-344822]) {
outline: 2px solid var(--c-primary-light, #d4a017);
outline-offset: 1px;
}
.hb__label:where([data-rk-344822]) {
font-size: 0.8125rem;
color: var(--c-text-2, #a3a3a3);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hb__track:where([data-rk-344822]) {
height: 10px;
border-radius: 4px;
background: rgb(var(--ink-rgb, 255 255 255) / 0.04);
overflow: hidden;
}
.hb__bar:where([data-rk-344822]) {
display: block;
height: 100%;
width: var(--hb-w);
border-radius: 0 4px 4px 0;
background: var(--hb-c);
opacity: var(--hb-o);
transform-origin: left;
animation: horizontal-bar-chart-hb-grow 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
animation-delay: calc(var(--hb-i) * 60ms);
}
.hb__value:where([data-rk-344822]) {
font-size: 0.8125rem;
font-weight: 600;
color: var(--c-text-1, #fff);
font-variant-numeric: tabular-nums;
min-width: 3.5rem;
text-align: right;
}
@keyframes horizontal-bar-chart-hb-grow {
from { transform: scaleX(0); }
}
@media (max-width: 480px) {
.hb__row:where([data-rk-344822]) { grid-template-columns: 1fr auto; }
.hb__track:where([data-rk-344822]) { grid-column: 1 / -1; grid-row: 2; }
}
@media (prefers-reduced-motion: reduce) {
.hb__bar:where([data-rk-344822]) { animation: none; }
}Quick Info
- Category
- Charts & Stats
- Filename
HorizontalBarChart.tsx- Dependencies
- None โ pure React + CSS
- Tags
- chartsvgzero-dependencybarhorizontalrankingtop-list