1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticas chartsvgzero-dependencylollipopdottargetscore

Lollipop Chart

Tallos finos con puntos de valor y línea de objetivo opcional; alternativa ligera a las barras.

Vista Previa en Vivo

Lighthouse performance

Score per page template

0 25 50 75 100 Home Docs Blog Pricing Dashboard Checkout Search
View data
ItemValueGoal
Home98Met
Docs96Met
Blog91Met
Pricing88Below
Dashboard74Below
Checkout81Below
Search69Below

Uso

Copia y pega este código directamente en tu página de Astro:

astro
---
import { LollipopChart } from 'astro-component-kit/charts';
---

<LollipopChart
  items={[
    { label: 'Home', value: 98 },
    { label: 'Search', value: 69 },
  ]}
  target={90}
  targetLabel="Goal"
/>
--- import { LollipopChart } from 'astro-component-kit/charts'; --- <LollipopChart items={[ { label: 'Home', value: 98 }, { label: 'Search', value: 69 }, ]} target={90} targetLabel="Goal" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/lib/LollipopChart.astro:

astro
---
/**
 * LollipopChart — Lightweight alternative to a bar chart: a thin stem with a dot
 * at the value. Optionally draws a target/benchmark line.
 *
 * @param {Array<{label: string, value: number}>} items - Values to plot.
 * @param {number} target - Optional benchmark value.
 * @param {string} targetLabel - Label for the benchmark line.
 * @param {string} prefix - Value prefix.
 * @param {string} suffix - Value suffix.
 * @param {string} color - Stem/dot color.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
import ChartFrame from './ChartFrame.astro';
import { scaleLinear, niceTicks, formatValue } from './chart-utils';

interface Props {
  title?: string;
  subtitle?: string;
  items?: Array<{ label: string; value: number }>;
  target?: number;
  targetLabel?: string;
  prefix?: string;
  suffix?: string;
  height?: number;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

const {
  title = 'Lighthouse performance',
  subtitle = 'Score per page template',
  items = [
    { label: 'Home', value: 98 },
    { label: 'Docs', value: 96 },
    { label: 'Blog', value: 91 },
    { label: 'Pricing', value: 88 },
    { label: 'Dashboard', value: 74 },
    { label: 'Checkout', value: 81 },
    { label: 'Search', value: 69 },
  ],
  target = 90,
  targetLabel = 'Goal',
  prefix = '',
  suffix = '',
  height = 270,
  color = 'var(--chart-1, #3987e5)',
  variant = 'glass',
  size = 'md',
  className = '',
} = Astro.props;

const W = 640;
const H = height;
const m = { t: 16, r: 12, b: 30, l: 40 };
const ticks = niceTicks(0, Math.max(...items.map((i) => i.value), target ?? 0), 4);
const y = scaleLinear(0, ticks[ticks.length - 1], H - m.b, m.t);
const band = (W - m.l - m.r) / items.length;
const fmt = (v: number) => formatValue(v, prefix, suffix);
---

<ChartFrame
  title={title}
  subtitle={subtitle}
  variant={variant}
  size={size}
  className={className}
  table={{ headers: ['Item', 'Value', targetLabel], rows: items.map((i) => [i.label, fmt(i.value), target !== undefined ? (i.value >= target ? 'Met' : 'Below') : '—']) }}
>
  <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: lollipop chart`}>
    {ticks.map((t) => (
      <g>
        <line class={t === 0 ? 'cf-axis' : 'cf-grid'} x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} />
        <text class="cf-label" x={m.l - 8} y={y(t) + 4} text-anchor="end">{fmt(t)}</text>
      </g>
    ))}
    {target !== undefined && (
      <g aria-hidden="true">
        <line x1={m.l} x2={W - m.r} y1={y(target)} y2={y(target)} stroke="var(--c-text-1, #fff)" stroke-opacity="0.55" stroke-width="1" stroke-dasharray="4 4" />
        <text class="cf-label" x={W - m.r} y={y(target) - 6} text-anchor="end">{targetLabel} {fmt(target)}</text>
      </g>
    )}
    {items.map((it, i) => {
      const cx = m.l + i * band + band / 2;
      const below = target !== undefined && target > it.value;
      return (
        <g class="cf-mark cf-dim" tabindex="0" role="img" aria-label={`${it.label}: ${fmt(it.value)}${below ? ', below goal' : ''}`}
          data-tip-title={it.label} data-tip={`${color}::${fmt(it.value)}${target !== undefined ? `|${below ? 'Below' : 'Meets'} ${targetLabel.toLowerCase()}` : ''}`}>
          <line x1={cx} x2={cx} y1={y(0)} y2={y(it.value)} stroke={color} stroke-width="2" stroke-opacity={below ? 0.5 : 1} />
          <circle cx={cx} cy={y(it.value)} r="7" fill={below ? 'var(--chart-surface, #0d0d0f)' : color} stroke={color} stroke-width="2.5" />
          <rect x={cx - band / 2} y={m.t} width={band} height={H - m.t - m.b} fill="transparent" />
          <text class="cf-label" x={cx} y={H - 10} text-anchor="middle">{it.label}</text>
        </g>
      );
    })}
  </svg>
</ChartFrame>
--- /** * LollipopChart — Lightweight alternative to a bar chart: a thin stem with a dot * at the value. Optionally draws a target/benchmark line. * * @param {Array<{label: string, value: number}>} items - Values to plot. * @param {number} target - Optional benchmark value. * @param {string} targetLabel - Label for the benchmark line. * @param {string} prefix - Value prefix. * @param {string} suffix - Value suffix. * @param {string} color - Stem/dot color. * @param {string} variant - Card style (glass|solid|ghost). * @param {string} size - Density (sm|md|lg). */ import ChartFrame from './ChartFrame.astro'; import { scaleLinear, niceTicks, formatValue } from './chart-utils'; interface Props { title?: string; subtitle?: string; items?: Array<{ label: string; value: number }>; target?: number; targetLabel?: string; prefix?: string; suffix?: string; height?: number; color?: string; variant?: 'glass' | 'solid' | 'ghost'; size?: 'sm' | 'md' | 'lg'; className?: string; } const { title = 'Lighthouse performance', subtitle = 'Score per page template', items = [ { label: 'Home', value: 98 }, { label: 'Docs', value: 96 }, { label: 'Blog', value: 91 }, { label: 'Pricing', value: 88 }, { label: 'Dashboard', value: 74 }, { label: 'Checkout', value: 81 }, { label: 'Search', value: 69 }, ], target = 90, targetLabel = 'Goal', prefix = '', suffix = '', height = 270, color = 'var(--chart-1, #3987e5)', variant = 'glass', size = 'md', className = '', } = Astro.props; const W = 640; const H = height; const m = { t: 16, r: 12, b: 30, l: 40 }; const ticks = niceTicks(0, Math.max(...items.map((i) => i.value), target ?? 0), 4); const y = scaleLinear(0, ticks[ticks.length - 1], H - m.b, m.t); const band = (W - m.l - m.r) / items.length; const fmt = (v: number) => formatValue(v, prefix, suffix); --- <ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} table={{ headers: ['Item', 'Value', targetLabel], rows: items.map((i) => [i.label, fmt(i.value), target !== undefined ? (i.value >= target ? 'Met' : 'Below') : '—']) }} > <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: lollipop chart`}> {ticks.map((t) => ( <g> <line class={t === 0 ? 'cf-axis' : 'cf-grid'} x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} /> <text class="cf-label" x={m.l - 8} y={y(t) + 4} text-anchor="end">{fmt(t)}</text> </g> ))} {target !== undefined && ( <g aria-hidden="true"> <line x1={m.l} x2={W - m.r} y1={y(target)} y2={y(target)} stroke="var(--c-text-1, #fff)" stroke-opacity="0.55" stroke-width="1" stroke-dasharray="4 4" /> <text class="cf-label" x={W - m.r} y={y(target) - 6} text-anchor="end">{targetLabel} {fmt(target)}</text> </g> )} {items.map((it, i) => { const cx = m.l + i * band + band / 2; const below = target !== undefined && target > it.value; return ( <g class="cf-mark cf-dim" tabindex="0" role="img" aria-label={`${it.label}: ${fmt(it.value)}${below ? ', below goal' : ''}`} data-tip-title={it.label} data-tip={`${color}::${fmt(it.value)}${target !== undefined ? `|${below ? 'Below' : 'Meets'} ${targetLabel.toLowerCase()}` : ''}`}> <line x1={cx} x2={cx} y1={y(0)} y2={y(it.value)} stroke={color} stroke-width="2" stroke-opacity={below ? 0.5 : 1} /> <circle cx={cx} cy={y(it.value)} r="7" fill={below ? 'var(--chart-surface, #0d0d0f)' : color} stroke={color} stroke-width="2.5" /> <rect x={cx - band / 2} y={m.t} width={band} height={H - m.t - m.b} fill="transparent" /> <text class="cf-label" x={cx} y={H - 10} text-anchor="middle">{it.label}</text> </g> ); })} </svg> </ChartFrame>

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
LollipopChart.astro
Dependencias
apexcharts (peer)
Etiquetas
chartsvgzero-dependencylollipopdottargetscore