Chart Tooltip
ChartTooltipContent: a floating surface listing every series at the hovered position, with optional indicator, label and formatter.
ChartTooltipContent is a floating (+2) surface that lists every series at the hovered position. Indicator, label and formatter are all optional. Installation and the shared API live on the Chart page.
Default
Section titled “Default”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Default </CardTitle> <CardDescription className="text-[13.5px]"> Default tooltip with ChartTooltipContent. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={<ChartTooltipContent />} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { labelFormatter: (i) => data[Number(i)]?.date ?? "",});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Default</CardTitle> <CardDescription class="text-[13.5px]">Default tooltip with ChartTooltipContent.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Default</Card.Title> <Card.Description class="text-[13.5px]">Default tooltip with ChartTooltipContent.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>Line Indicator
Section titled “Line Indicator”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Line Indicator </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with line indicator. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={<ChartTooltipContent indicator="line" />} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { indicator: "line", labelFormatter: (i) => data[Number(i)]?.date ?? "",});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Line Indicator</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with line indicator.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Line Indicator</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with line indicator.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="line" /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>No Indicator
Section titled “No Indicator”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - No Indicator </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with no indicator. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={<ChartTooltipContent indicator="none" />} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { indicator: "none", labelFormatter: (i) => data[Number(i)]?.date ?? "",});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - No Indicator</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with no indicator.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - No Indicator</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with no indicator.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="none" /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>Custom label
Section titled “Custom label”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, activities: { label: "Activities" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Custom label </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with custom label from chartConfig. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={ <ChartTooltipContent indicator="line" labelKey="activities" /> } /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, activities: { label: "Activities" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { indicator: "line", labelKey: "activities",});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Custom label</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with custom label from chartConfig.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, activities: { label: "Activities" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Custom label</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with custom label from chartConfig.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="line" labelKey="activities" /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>Label Formatter
Section titled “Label Formatter”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Label Formatter </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with label formatter. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={ <ChartTooltipContent indicator="line" labelFormatter={(value) => new Date(value).toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric", timeZone: "UTC", }) } /> } /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { indicator: "line", labelFormatter: (i) => new Date(data[Number(i)]?.date ?? "").toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }),});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Label Formatter</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with label formatter.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Label Formatter</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with label formatter.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="line" labelFormatter={(value: string) => new Date(value).toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" })} /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>No Label
Section titled “No Label”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - No Label </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with no label. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={<ChartTooltipContent hideLabel indicator="line" />} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { indicator: "line", hideLabel: true,});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - No Label</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with no label.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - No Label</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with no label.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip hideLabel indicator="line" /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>Formatter
Section titled “Formatter”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import type { CSSProperties } from "react";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Formatter </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with custom formatter. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={ <ChartTooltipContent labelFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } formatter={(value, name, item) => ( <> <div className="size-2 shrink-0 rounded-[2px] bg-(--color-bg)" style={{ "--color-bg": item.color } as CSSProperties} /> <span className="flex-1 text-muted-foreground"> {config[name as keyof typeof config]?.label ?? name} </span> <span className="ml-3 flex items-baseline gap-0.5 font-semibold text-foreground tabular-nums"> {value} <span className="font-normal text-muted-foreground"> kcal </span> </span> </> )} /> } /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { defineComponent, h, type PropType } from "vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { chartColor, type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");
// The row layout (value right-aligned with a unit, derived total row) is composed with the default slot of ChartTooltipContent.const KcalTooltip = defineComponent({ props: { payload: { type: Object as PropType<Row>, default: () => ({}) }, config: { type: Object as PropType<ChartConfig>, default: () => ({}) }, x: { type: Number, default: 0 }, }, setup(props) { return () => { const row = props.payload; const rows = (["running", "swimming"] as const).map((key) => h("div", { class: "flex w-full items-center gap-2" }, [ h("div", { class: "size-2 shrink-0 rounded-[2px] bg-(--color-bg)", style: "--color-bg: " + chartColor(props.config, key), }), h("span", { class: "flex-1 text-muted-foreground" }, String(props.config[key]?.label)), h( "span", { class: "ml-3 flex items-baseline gap-0.5 whitespace-nowrap font-semibold text-foreground tabular-nums" }, [String(row[key]), h("span", { class: "font-normal text-muted-foreground" }, "kcal")], ), ]), ); return h( ChartTooltipContent, { payload: props.payload, config: props.config, x: props.x }, { default: () => [ h("div", { class: "font-medium text-foreground" }, weekday(row.date)), h("div", { class: "grid gap-1.5" }, rows), ], }, ); }; },});const template = componentToString(config, KcalTooltip as never);</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Formatter</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with custom formatter.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });
</script>
{#snippet formatter({ name, value, item })} <div class="size-2 shrink-0 rounded-[2px] bg-(--color-bg)" style="--color-bg: {item.key === "running" ? config.running.color : config.swimming.color}"></div> <span class="flex-1 text-muted-foreground">{name}</span> <span class="ml-3 flex items-baseline gap-0.5 font-semibold text-foreground tabular-nums"> {value} <span class="font-normal text-muted-foreground">kcal</span> </span>{/snippet}
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Formatter</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with custom formatter.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip labelFormatter={(value: string) => weekday(value)} {formatter} /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";import { IconPlaceholder } from "@/edmi/icon-placeholder";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)", icon: RunningIcon }, swimming: { label: "Swimming", color: "var(--chart-2)", icon: SwimmingIcon },} satisfies ChartConfig;
function RunningIcon() { return ( <IconPlaceholder lucide="FootprintsIcon" tabler="IconRun" hugeicons="RunningShoesIcon" phosphor="PersonSimpleRunIcon" remixicon="RiRunLine" /> );}
function SwimmingIcon() { return ( <IconPlaceholder lucide="WavesIcon" tabler="IconSwimming" hugeicons="SwimmingIcon" phosphor="SwimmingPoolIcon" remixicon="RiWaterFlashLine" /> );}
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Icons </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with icons. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={ <ChartTooltipContent labelFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> } /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { ActivityIcon, HeartIcon } from "@lucide/vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)", icon: ActivityIcon }, swimming: { label: "Swimming", color: "var(--chart-2)", icon: HeartIcon },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");const template = componentToString(config, ChartTooltipContent, { labelFormatter: (i) => weekday(data[Number(i)]?.date),});</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Icons</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with icons.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import PersonSimpleRunIcon from "phosphor-svelte/lib/PersonSimpleRunIcon"; import SwimmingPoolIcon from "phosphor-svelte/lib/SwimmingPoolIcon"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)", icon: PersonSimpleRunIcon }, swimming: { label: "Swimming", color: "var(--chart-2)", icon: SwimmingPoolIcon }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Icons</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with icons.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip labelFormatter={(value: string) => weekday(value)} /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>Advanced
Section titled “Advanced”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import type { CSSProperties } from "react";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
export default function Demo() { return ( <Card className="w-full max-w-sm gap-0 py-0"> <CardHeader className="gap-1 px-6 pt-5.5 pb-0"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Tooltip - Advanced </CardTitle> <CardDescription className="text-[13.5px]"> Tooltip with a total row. </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-4"> <ChartContainer config={config} className="aspect-auto h-44 w-full"> <BarChart accessibilityLayer data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } /> <Bar dataKey="running" stackId="a" fill="var(--color-running)" stroke="var(--card)" strokeWidth={2} maxBarSize={24} /> <Bar dataKey="swimming" stackId="a" fill="var(--color-swimming)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> <ChartTooltip defaultIndex={1} content={ <ChartTooltipContent labelFormatter={(value) => new Date(value).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC", }) } formatter={(value, name, item, index) => ( <div className="grid w-full gap-1.5"> <div className="flex w-full items-center gap-2"> <div className="size-2 shrink-0 rounded-[2px] bg-(--color-bg)" style={{ "--color-bg": item.color } as CSSProperties} /> <span className="flex-1 text-muted-foreground"> {config[name as keyof typeof config]?.label ?? name} </span> <span className="ml-3 flex items-baseline gap-0.5 font-semibold text-foreground tabular-nums"> {value} <span className="font-normal text-muted-foreground"> kcal </span> </span> </div> {index === 1 && ( <div className="flex items-center border-t pt-1.5 font-medium text-foreground"> Total <span className="ml-auto flex items-baseline gap-0.5 font-semibold tabular-nums"> {item.payload.running + item.payload.swimming} <span className="font-normal text-muted-foreground"> kcal </span> </span> </div> )} </div> )} /> } /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { defineComponent, h, type PropType } from "vue";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { chartColor, type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { date: string; running: number; swimming: number };
const data: Row[] = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 },];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = ["var(--chart-1)", "var(--chart-2)"];const weekday = (date = "") => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });const x = (_: Row, i: number) => i;const tickFormat = (i: number) => (Number.isInteger(i) ? weekday(data[i]?.date) : "");
// The row layout (value right-aligned with a unit, derived total row) is composed with the default slot of ChartTooltipContent.const KcalTooltip = defineComponent({ props: { payload: { type: Object as PropType<Row>, default: () => ({}) }, config: { type: Object as PropType<ChartConfig>, default: () => ({}) }, x: { type: Number, default: 0 }, }, setup(props) { return () => { const row = props.payload; const rows = (["running", "swimming"] as const).map((key) => h("div", { class: "flex w-full items-center gap-2" }, [ h("div", { class: "size-2 shrink-0 rounded-[2px] bg-(--color-bg)", style: "--color-bg: " + chartColor(props.config, key), }), h("span", { class: "flex-1 text-muted-foreground" }, String(props.config[key]?.label)), h( "span", { class: "ml-3 flex items-baseline gap-0.5 whitespace-nowrap font-semibold text-foreground tabular-nums" }, [String(row[key]), h("span", { class: "font-normal text-muted-foreground" }, "kcal")], ), ]), ); rows.push( h("div", { class: "flex items-center border-t pt-1.5 font-medium text-foreground" }, [ "Total", h("span", { class: "ml-auto flex items-baseline gap-0.5 font-semibold tabular-nums" }, [ String(row.running + row.swimming), h("span", { class: "font-normal text-muted-foreground" }, "kcal"), ]), ]), ); return h( ChartTooltipContent, { payload: props.payload, config: props.config, x: props.x }, { default: () => [ h("div", { class: "font-medium text-foreground" }, weekday(row.date)), h("div", { class: "grid gap-1.5" }, rows), ], }, ); }; },});const template = componentToString(config, KcalTooltip as never);</script>
<template> <Card class="w-full max-w-sm gap-0 py-0"> <CardHeader class="gap-1 px-6 pt-5.5 pb-0"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Tooltip - Advanced</CardTitle> <CardDescription class="text-[13.5px]">Tooltip with a total row.</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-4"> <ChartContainer :config="config" class="aspect-auto h-44 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]"> <VisStackedBar :x="x" :y="[(d: Row) => d.running, (d: Row) => d.swimming]" :color="(_: Row, i: number) => colors[i]" :bar-max-width="24" :bar-padding="0.3" :rounded-corners="4" /> <VisAxis type="y" :num-ticks="4" :tick-format="() => ''" :tick-line="false" :domain-line="false" /> <VisAxis type="x" :x="x" :tick-values="[0, 1, 2, 3, 4, 5]" :tick-format="tickFormat" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.running, (d: Row) => d.running + d.swimming]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> </ChartContainer> </CardContent> </Card></template><script lang="ts"> import { BarChart } from "layerchart"; import * as Card from "$lib/components/ui/card"; import * as Chart from "$lib/components/ui/chart";
const data = [ { date: "2024-07-15", running: 450, swimming: 300 }, { date: "2024-07-16", running: 380, swimming: 420 }, { date: "2024-07-17", running: 520, swimming: 120 }, { date: "2024-07-18", running: 140, swimming: 550 }, { date: "2024-07-19", running: 600, swimming: 350 }, { date: "2024-07-20", running: 480, swimming: 400 }, ];
const config = { running: { label: "Running", color: "var(--chart-1)" }, swimming: { label: "Swimming", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;
const weekday = (date: string) => new Date(date).toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" });
</script>
{#snippet formatter({ name, value, item, index, payload })} <div class="grid w-full gap-1.5"> <div class="flex w-full items-center gap-2"> <div class="size-2 shrink-0 rounded-[2px] bg-(--color-bg)" style="--color-bg: {item.key === "running" ? config.running.color : config.swimming.color}"></div> <span class="flex-1 text-muted-foreground">{name}</span> <span class="ml-3 flex items-baseline gap-0.5 font-semibold text-foreground tabular-nums"> {value} <span class="font-normal text-muted-foreground">kcal</span> </span> </div> {#if index === payload.length - 1} <div class="flex items-center border-t pt-1.5 font-medium text-foreground"> Total <span class="ml-auto flex items-baseline gap-0.5 font-semibold tabular-nums"> {payload.reduce((sum, p) => sum + Number(p.value), 0)} <span class="font-normal text-muted-foreground">kcal</span> </span> </div> {/if} </div>{/snippet}
<Card.Root class="w-full max-w-sm gap-0 py-0"> <Card.Header class="gap-1 px-6 pt-5.5 pb-0"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Tooltip - Advanced</Card.Title> <Card.Description class="text-[13.5px]">Tooltip with a total row.</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-4"> <Chart.Container {config} class="aspect-auto h-44 w-full"> <BarChart {data} x="date" seriesLayout="stack" bandPadding={0.35} series={[ { key: "running", label: config.running.label, color: "var(--color-running)" }, { key: "swimming", label: config.swimming.label, color: "var(--color-swimming)" }, ]} props={{ bars: { radius: 4, rounded: "edge", stroke: "var(--card)", strokeWidth: 2 }, xAxis: { format: (v: string) => weekday(v) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip labelFormatter={(value: string) => weekday(value)} {formatter} /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>