Appearance
@vibe-labs/design-vue-charts
Vue 3 components for lightweight CSS-driven charts. No D3 dependency — for data-dense interactive charting, see @vibe-labs/design-vue-graphs.
Installation
ts
import { VibeChartHeatmap, VibeChartLegend, VibeChartLegendGradient, useHeatmapIntensity } from "@vibe-labs/design-vue-charts";Requires the CSS layer from @vibe-labs/design-components-charts.
Components
VibeChartHeatmap
Renders a 2D heatmap grid from numeric data. Automatically maps values to 5 intensity levels (0–4).
Usage
vue
<VibeChartHeatmap
:rows="[
[0, 2, 5, 8, 3],
[1, 4, 7, 2, 6],
[3, 0, 1, 9, 4],
]"
:x-labels="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
:y-labels="['Week 1', 'Week 2', 'Week 3']"
size="md"
color-scale="accent"
:rounded="true"
:interactive="true"
aria-label="Weekly activity heatmap"
@cell-click="onCellClick"
>
<template #footer>
<VibeChartLegendGradient min-label="None" max-label="High" />
</template>
</VibeChartHeatmap>Rich Data
Pass HeatmapCell objects instead of raw numbers for per-cell labels:
vue
<VibeChartHeatmap
:rows="[
[
{ value: 5, label: '5 commits' },
{ value: 0, label: 'No activity' },
],
[
{ value: 12, label: '12 commits' },
{ value: 3, label: '3 commits' },
],
]"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows | (number | HeatmapCell)[][] | required | 2D data grid |
xLabels | string[] | — | Column header labels |
yLabels | string[] | — | Row header labels |
min | number | auto | Override minimum value for intensity mapping |
max | number | auto | Override maximum value for intensity mapping |
size | "sm" | "md" | "lg" | "md" | Cell size + gap |
colorScale | "accent" | "success" | "warning" | "danger" | "info" | "accent" | Color theme |
rounded | boolean | false | Round cell corners |
seamless | boolean | false | Remove gap between cells |
interactive | boolean | false | Enable hover/focus/click on cells |
ariaLabel | string | — | Accessible description |
Events
| Event | Payload | Description |
|---|---|---|
cellClick | HeatmapCellEvent | Cell clicked (when interactive) |
cellHover | HeatmapCellEvent | Cell hovered (when interactive) |
Slots
| Slot | Scoped Props | Description |
|---|---|---|
cell | { row, col, value, label, intensity } | Custom cell content |
footer | — | Below the grid (legend, controls, etc.) |
VibeChartLegend
Categorical legend with color swatches.
Usage
vue
<VibeChartLegend
layout="horizontal"
:items="[
{ label: 'Revenue', color: 'var(--color-accent)' },
{ label: 'Costs', color: 'var(--color-danger)' },
]"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | VibeChartLegendItem[] | required | Array of { label, color } |
layout | "horizontal" | "vertical" | "horizontal" | Layout direction |
VibeChartLegendGradient
Continuous gradient legend for heatmap intensity scales.
Usage
vue
<VibeChartLegendGradient min-label="Low" max-label="High" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
minLabel | string | "Less" | Low-end label |
maxLabel | string | "More" | High-end label |
Composables
useHeatmapIntensity
Maps raw numeric data to intensity buckets (0–4). Used internally by VibeChartHeatmap but exported for custom implementations.
ts
import { useHeatmapIntensity } from "@vibe-labs/design-vue-charts";
const { grid, colCount, min, max } = useHeatmapIntensity({
rows: () => data.value,
min: () => 0,
max: () => 100,
});Returns
| Property | Type | Description |
|---|---|---|
grid | ComputedRef<{ row, col, value, label?, intensity }[][]> | Mapped 2D grid |
colCount | ComputedRef<number> | Number of columns |
min | ComputedRef<number> | Resolved minimum |
max | ComputedRef<number> | Resolved maximum |
Dependencies
@vibe-labs/design-components-charts— CSS + types@vibe-labs/core— shared utilitiesvue(peer) — ^3.5.18
Build
bash
npm run buildBuilt with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.
Usage Guide
Setup
ts
import { VibeChartHeatmap, VibeChartLegend, VibeChartLegendGradient } from "@vibe-labs/design-vue-charts";
import "@vibe-labs/design-components-charts";VibeChartHeatmap
Weekly activity heatmap
vue
<script setup lang="ts">
import { VibeChartHeatmap, VibeChartLegendGradient } from "@vibe-labs/design-vue-charts";
import { ref } from "vue";
// 4 weeks × 7 days of commit counts
const activityData = ref([
[0, 2, 5, 8, 3, 0, 1],
[1, 4, 7, 2, 6, 3, 2],
[3, 0, 1, 9, 4, 5, 0],
[2, 6, 3, 1, 8, 4, 7],
]);
const xLabels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const yLabels = ["Week 1", "Week 2", "Week 3", "Week 4"];
</script>
<template>
<VibeChartHeatmap
:rows="activityData"
:x-labels="xLabels"
:y-labels="yLabels"
color-scale="accent"
:rounded="true"
:interactive="true"
aria-label="Monthly git activity"
>
<template #footer>
<VibeChartLegendGradient min-label="No activity" max-label="High activity" />
</template>
</VibeChartHeatmap>
</template>Heatmap with rich cell data and click handler
vue
<script setup lang="ts">
import { VibeChartHeatmap } from "@vibe-labs/design-vue-charts";
import type { HeatmapCellEvent } from "@vibe-labs/design-vue-charts";
const rows = [
[
{ value: 12, label: "12 plays" },
{ value: 45, label: "45 plays" },
{ value: 3, label: "3 plays" },
],
[
{ value: 28, label: "28 plays" },
{ value: 0, label: "No plays" },
{ value: 67, label: "67 plays" },
],
];
function onCellClick(e: HeatmapCellEvent) {
console.log(`Row ${e.row}, Col ${e.col}: ${e.label ?? e.value}`);
}
</script>
<template>
<VibeChartHeatmap
:rows="rows"
:x-labels="['Mon', 'Wed', 'Fri']"
:y-labels="['This week', 'Last week']"
color-scale="success"
size="lg"
:interactive="true"
:rounded="true"
@cell-click="onCellClick"
/>
</template>Categorical legend
vue
<script setup lang="ts">
import { VibeChartLegend } from "@vibe-labs/design-vue-charts";
const legendItems = [
{ label: "Revenue", color: "var(--color-accent)" },
{ label: "Costs", color: "var(--color-danger)" },
{ label: "Profit", color: "var(--color-success)" },
];
</script>
<template>
<VibeChartLegend :items="legendItems" layout="horizontal" />
</template>Composables
useHeatmapIntensity for custom rendering
vue
<script setup lang="ts">
import { useHeatmapIntensity } from "@vibe-labs/design-vue-charts";
import { ref } from "vue";
const rawData = ref([[5, 20, 0, 15], [10, 3, 8, 25]]);
const { grid, min, max } = useHeatmapIntensity({
rows: () => rawData.value,
});
// grid.value is a 2D array of { row, col, value, intensity (0-4) }
</script>Common Patterns
Heatmap with custom cell slot
vue
<template>
<VibeChartHeatmap :rows="rows" :interactive="true">
<template #cell="{ value, intensity }">
<span :title="`${value} events`" :data-intensity="intensity" />
</template>
</VibeChartHeatmap>
</template>