Bar Chart
Compare magnitudes across categories: capped 24px bars, a 4px rounded data end and a square baseline.
Bar charts compare magnitudes across categories. Bars are capped at 24px with a rounded data end and a square baseline. Installation and the shared API live on the Chart page.
Interactive
Section titled “Interactive”import { Card, CardContent, CardDescription, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartStatWell, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { useState } from "react";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { date: "2024-04-01", desktop: 252, mobile: 342 }, { date: "2024-04-02", desktop: 254, mobile: 260 }, { date: "2024-04-03", desktop: 305, mobile: 300 }, { date: "2024-04-04", desktop: 322, mobile: 338 }, { date: "2024-04-05", desktop: 321, mobile: 313 }, { date: "2024-04-06", desktop: 283, mobile: 279 }, { date: "2024-04-07", desktop: 290, mobile: 371 }, { date: "2024-04-08", desktop: 351, mobile: 384 }, { date: "2024-04-09", desktop: 384, mobile: 338 }, { date: "2024-04-10", desktop: 302, mobile: 346 }, { date: "2024-04-11", desktop: 337, mobile: 360 }, { date: "2024-04-12", desktop: 307, mobile: 261 }, { date: "2024-04-13", desktop: 254, mobile: 273 }, { date: "2024-04-14", desktop: 214, mobile: 315 }, { date: "2024-04-15", desktop: 244, mobile: 362 }, { date: "2024-04-16", desktop: 252, mobile: 280 }, { date: "2024-04-17", desktop: 277, mobile: 222 }, { date: "2024-04-18", desktop: 311, mobile: 242 }, { date: "2024-04-19", desktop: 185, mobile: 242 }, { date: "2024-04-20", desktop: 247, mobile: 210 }, { date: "2024-04-21", desktop: 194, mobile: 253 }, { date: "2024-04-22", desktop: 159, mobile: 243 }, { date: "2024-04-23", desktop: 222, mobile: 190 }, { date: "2024-04-24", desktop: 237, mobile: 227 }, { date: "2024-04-25", desktop: 235, mobile: 160 }, { date: "2024-04-26", desktop: 211, mobile: 180 }, { date: "2024-04-27", desktop: 192, mobile: 143 }, { date: "2024-04-28", desktop: 235, mobile: 206 }, { date: "2024-04-29", desktop: 234, mobile: 223 }, { date: "2024-04-30", desktop: 212, mobile: 196 }, { date: "2024-05-01", desktop: 210, mobile: 270 }, { date: "2024-05-02", desktop: 283, mobile: 224 }, { date: "2024-05-03", desktop: 249, mobile: 254 }, { date: "2024-05-04", desktop: 238, mobile: 174 }, { date: "2024-05-05", desktop: 223, mobile: 205 }, { date: "2024-05-06", desktop: 250, mobile: 260 }, { date: "2024-05-07", desktop: 306, mobile: 345 }, { date: "2024-05-08", desktop: 287, mobile: 243 }, { date: "2024-05-09", desktop: 288, mobile: 326 }, { date: "2024-05-10", desktop: 371, mobile: 327 }, { date: "2024-05-11", desktop: 320, mobile: 323 }, { date: "2024-05-12", desktop: 354, mobile: 301 }, { date: "2024-05-13", desktop: 378, mobile: 374 }, { date: "2024-05-14", desktop: 386, mobile: 383 }, { date: "2024-05-15", desktop: 310, mobile: 375 }, { date: "2024-05-16", desktop: 365, mobile: 290 }, { date: "2024-05-17", desktop: 373, mobile: 358 }, { date: "2024-05-18", desktop: 347, mobile: 326 }, { date: "2024-05-19", desktop: 350, mobile: 264 }, { date: "2024-05-20", desktop: 276, mobile: 266 }, { date: "2024-05-21", desktop: 347, mobile: 219 }, { date: "2024-05-22", desktop: 231, mobile: 371 }, { date: "2024-05-23", desktop: 240, mobile: 310 }, { date: "2024-05-24", desktop: 253, mobile: 342 }, { date: "2024-05-25", desktop: 231, mobile: 255 }, { date: "2024-05-26", desktop: 190, mobile: 223 }, { date: "2024-05-27", desktop: 214, mobile: 272 }, { date: "2024-05-28", desktop: 285, mobile: 194 }, { date: "2024-05-29", desktop: 181, mobile: 235 }, { date: "2024-05-30", desktop: 223, mobile: 210 }, { date: "2024-05-31", desktop: 171, mobile: 155 }, { date: "2024-06-01", desktop: 244, mobile: 249 }, { date: "2024-06-02", desktop: 225, mobile: 252 }, { date: "2024-06-03", desktop: 181, mobile: 170 }, { date: "2024-06-04", desktop: 188, mobile: 198 }, { date: "2024-06-05", desktop: 177, mobile: 209 }, { date: "2024-06-06", desktop: 155, mobile: 207 }, { date: "2024-06-07", desktop: 171, mobile: 163 }, { date: "2024-06-08", desktop: 244, mobile: 232 }, { date: "2024-06-09", desktop: 208, mobile: 217 }, { date: "2024-06-10", desktop: 185, mobile: 293 }, { date: "2024-06-11", desktop: 233, mobile: 272 }, { date: "2024-06-12", desktop: 252, mobile: 336 }, { date: "2024-06-13", desktop: 334, mobile: 282 }, { date: "2024-06-14", desktop: 329, mobile: 265 }, { date: "2024-06-15", desktop: 342, mobile: 260 }, { date: "2024-06-16", desktop: 333, mobile: 339 }, { date: "2024-06-17", desktop: 385, mobile: 328 }, { date: "2024-06-18", desktop: 345, mobile: 266 }, { date: "2024-06-19", desktop: 321, mobile: 362 }, { date: "2024-06-20", desktop: 336, mobile: 272 }, { date: "2024-06-21", desktop: 289, mobile: 342 }, { date: "2024-06-22", desktop: 306, mobile: 340 }, { date: "2024-06-23", desktop: 296, mobile: 286 }, { date: "2024-06-24", desktop: 287, mobile: 269 }, { date: "2024-06-25", desktop: 353, mobile: 313 }, { date: "2024-06-26", desktop: 290, mobile: 291 }, { date: "2024-06-27", desktop: 305, mobile: 325 }, { date: "2024-06-28", desktop: 380, mobile: 381 }, { date: "2024-06-29", desktop: 302, mobile: 261 }, { date: "2024-06-30", desktop: 279, mobile: 297 },];
const config = { views: { label: "Page Views" }, desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-1)" },} satisfies ChartConfig;
const totals = { desktop: data.reduce((sum, d) => sum + d.desktop, 0), mobile: data.reduce((sum, d) => sum + d.mobile, 0),};
export default function Demo() { const [active, setActive] = useState<"desktop" | "mobile">("desktop");
return ( <Card className="w-full max-w-4xl gap-0 py-0"> <div className="flex flex-col items-stretch border-b sm:flex-row"> <div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5"> <CardTitle className="font-semibold text-base tracking-[-0.2px]"> Bar Chart - Interactive </CardTitle> <CardDescription className="text-[13.5px]"> Showing total visitors for the last 3 months </CardDescription> </div> <div className="flex"> {(["desktop", "mobile"] as const).map((key) => ( <ChartStatWell key={key} active={active === key} onClick={() => setActive(key)} > {config[key].label} <b>{totals[key].toLocaleString()}</b> </ChartStatWell> ))} </div> </div> <CardContent className="px-5 pt-5 pb-5"> <ChartContainer config={config} className="aspect-auto h-60 w-full"> <BarChart data={data} margin={{ left: 4, right: 4 }}> <CartesianGrid vertical={false} /> <XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} minTickGap={32} tickFormatter={(value) => new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC", }) } /> <ChartTooltip cursor={false} content={ <ChartTooltipContent className="w-36" nameKey="views" labelFormatter={(value) => new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC", }) } /> } /> <Bar dataKey={active} fill={`var(--color-${active})`} radius={[4, 4, 0, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { computed, ref } from "vue";import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardTitle } from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartStatWell, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Day = { date: string; desktop: number; mobile: number };type Row = { date: string; views: number };
const daily: Day[] = [ { date: "2024-04-01", desktop: 252, mobile: 342 }, { date: "2024-04-02", desktop: 254, mobile: 260 }, { date: "2024-04-03", desktop: 305, mobile: 300 }, { date: "2024-04-04", desktop: 322, mobile: 338 }, { date: "2024-04-05", desktop: 321, mobile: 313 }, { date: "2024-04-06", desktop: 283, mobile: 279 }, { date: "2024-04-07", desktop: 290, mobile: 371 }, { date: "2024-04-08", desktop: 351, mobile: 384 }, { date: "2024-04-09", desktop: 384, mobile: 338 }, { date: "2024-04-10", desktop: 302, mobile: 346 }, { date: "2024-04-11", desktop: 337, mobile: 360 }, { date: "2024-04-12", desktop: 307, mobile: 261 }, { date: "2024-04-13", desktop: 254, mobile: 273 }, { date: "2024-04-14", desktop: 214, mobile: 315 }, { date: "2024-04-15", desktop: 244, mobile: 362 }, { date: "2024-04-16", desktop: 252, mobile: 280 }, { date: "2024-04-17", desktop: 277, mobile: 222 }, { date: "2024-04-18", desktop: 311, mobile: 242 }, { date: "2024-04-19", desktop: 185, mobile: 242 }, { date: "2024-04-20", desktop: 247, mobile: 210 }, { date: "2024-04-21", desktop: 194, mobile: 253 }, { date: "2024-04-22", desktop: 159, mobile: 243 }, { date: "2024-04-23", desktop: 222, mobile: 190 }, { date: "2024-04-24", desktop: 237, mobile: 227 }, { date: "2024-04-25", desktop: 235, mobile: 160 }, { date: "2024-04-26", desktop: 211, mobile: 180 }, { date: "2024-04-27", desktop: 192, mobile: 143 }, { date: "2024-04-28", desktop: 235, mobile: 206 }, { date: "2024-04-29", desktop: 234, mobile: 223 }, { date: "2024-04-30", desktop: 212, mobile: 196 }, { date: "2024-05-01", desktop: 210, mobile: 270 }, { date: "2024-05-02", desktop: 283, mobile: 224 }, { date: "2024-05-03", desktop: 249, mobile: 254 }, { date: "2024-05-04", desktop: 238, mobile: 174 }, { date: "2024-05-05", desktop: 223, mobile: 205 }, { date: "2024-05-06", desktop: 250, mobile: 260 }, { date: "2024-05-07", desktop: 306, mobile: 345 }, { date: "2024-05-08", desktop: 287, mobile: 243 }, { date: "2024-05-09", desktop: 288, mobile: 326 }, { date: "2024-05-10", desktop: 371, mobile: 327 }, { date: "2024-05-11", desktop: 320, mobile: 323 }, { date: "2024-05-12", desktop: 354, mobile: 301 }, { date: "2024-05-13", desktop: 378, mobile: 374 }, { date: "2024-05-14", desktop: 386, mobile: 383 }, { date: "2024-05-15", desktop: 310, mobile: 375 }, { date: "2024-05-16", desktop: 365, mobile: 290 }, { date: "2024-05-17", desktop: 373, mobile: 358 }, { date: "2024-05-18", desktop: 347, mobile: 326 }, { date: "2024-05-19", desktop: 350, mobile: 264 }, { date: "2024-05-20", desktop: 276, mobile: 266 }, { date: "2024-05-21", desktop: 347, mobile: 219 }, { date: "2024-05-22", desktop: 231, mobile: 371 }, { date: "2024-05-23", desktop: 240, mobile: 310 }, { date: "2024-05-24", desktop: 253, mobile: 342 }, { date: "2024-05-25", desktop: 231, mobile: 255 }, { date: "2024-05-26", desktop: 190, mobile: 223 }, { date: "2024-05-27", desktop: 214, mobile: 272 }, { date: "2024-05-28", desktop: 285, mobile: 194 }, { date: "2024-05-29", desktop: 181, mobile: 235 }, { date: "2024-05-30", desktop: 223, mobile: 210 }, { date: "2024-05-31", desktop: 171, mobile: 155 }, { date: "2024-06-01", desktop: 244, mobile: 249 }, { date: "2024-06-02", desktop: 225, mobile: 252 }, { date: "2024-06-03", desktop: 181, mobile: 170 }, { date: "2024-06-04", desktop: 188, mobile: 198 }, { date: "2024-06-05", desktop: 177, mobile: 209 }, { date: "2024-06-06", desktop: 155, mobile: 207 }, { date: "2024-06-07", desktop: 171, mobile: 163 }, { date: "2024-06-08", desktop: 244, mobile: 232 }, { date: "2024-06-09", desktop: 208, mobile: 217 }, { date: "2024-06-10", desktop: 185, mobile: 293 }, { date: "2024-06-11", desktop: 233, mobile: 272 }, { date: "2024-06-12", desktop: 252, mobile: 336 }, { date: "2024-06-13", desktop: 334, mobile: 282 }, { date: "2024-06-14", desktop: 329, mobile: 265 }, { date: "2024-06-15", desktop: 342, mobile: 260 }, { date: "2024-06-16", desktop: 333, mobile: 339 }, { date: "2024-06-17", desktop: 385, mobile: 328 }, { date: "2024-06-18", desktop: 345, mobile: 266 }, { date: "2024-06-19", desktop: 321, mobile: 362 }, { date: "2024-06-20", desktop: 336, mobile: 272 }, { date: "2024-06-21", desktop: 289, mobile: 342 }, { date: "2024-06-22", desktop: 306, mobile: 340 }, { date: "2024-06-23", desktop: 296, mobile: 286 }, { date: "2024-06-24", desktop: 287, mobile: 269 }, { date: "2024-06-25", desktop: 353, mobile: 313 }, { date: "2024-06-26", desktop: 290, mobile: 291 }, { date: "2024-06-27", desktop: 305, mobile: 325 }, { date: "2024-06-28", desktop: 380, mobile: 381 }, { date: "2024-06-29", desktop: 302, mobile: 261 }, { date: "2024-06-30", desktop: 279, mobile: 297 },];
const config = { views: { label: "Page Views", color: "var(--chart-1)" },} satisfies ChartConfig;
const labels = { desktop: "Desktop", mobile: "Mobile" } as const;const totals = { desktop: daily.reduce((sum, d) => sum + d.desktop, 0), mobile: daily.reduce((sum, d) => sum + d.mobile, 0),};
const active = ref<"desktop" | "mobile">("desktop");const data = computed<Row[]>(() => daily.map((d) => ({ date: d.date, views: d[active.value] })));
const formatDate = (i: number | Date) => new Date(daily[Number(i)]?.date ?? 0).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC", });const x = (_: Row, i: number) => i;const template = componentToString(config, ChartTooltipContent, { indicator: "dot", labelFormatter: formatDate,});</script>
<template> <Card class="w-full max-w-4xl gap-0 py-0"> <div class="flex flex-col items-stretch border-b sm:flex-row"> <div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5"> <CardTitle class="font-semibold text-base tracking-[-0.2px]">Bar Chart - Interactive</CardTitle> <CardDescription class="text-[13.5px]">Showing total visitors for the last 3 months</CardDescription> </div> <div class="flex"> <ChartStatWell v-for="key in (['desktop', 'mobile'] as const)" :key="key" :active="active === key" @click="active = key" > {{ labels[key] }} <b>{{ totals[key].toLocaleString() }}</b> </ChartStatWell> </div> </div> <CardContent class="px-5 pt-5 pb-5"> <ChartContainer :config="config" class="aspect-auto h-60 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ left: 4, right: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisGroupedBar :x="x" :y="(d: Row) => d.views" :color="config.views.color" :rounded-corners="2" :group-max-width="24" :group-padding="0.25" /> <VisAxis type="x" :x="x" :tick-format="formatDate" :num-ticks="9" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="(d: Row) => d.views" :template="template" :color="() => config.views.color" /> </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 daily = [ { date: "2024-04-01", desktop: 252, mobile: 342 }, { date: "2024-04-02", desktop: 254, mobile: 260 }, { date: "2024-04-03", desktop: 305, mobile: 300 }, { date: "2024-04-04", desktop: 322, mobile: 338 }, { date: "2024-04-05", desktop: 321, mobile: 313 }, { date: "2024-04-06", desktop: 283, mobile: 279 }, { date: "2024-04-07", desktop: 290, mobile: 371 }, { date: "2024-04-08", desktop: 351, mobile: 384 }, { date: "2024-04-09", desktop: 384, mobile: 338 }, { date: "2024-04-10", desktop: 302, mobile: 346 }, { date: "2024-04-11", desktop: 337, mobile: 360 }, { date: "2024-04-12", desktop: 307, mobile: 261 }, { date: "2024-04-13", desktop: 254, mobile: 273 }, { date: "2024-04-14", desktop: 214, mobile: 315 }, { date: "2024-04-15", desktop: 244, mobile: 362 }, { date: "2024-04-16", desktop: 252, mobile: 280 }, { date: "2024-04-17", desktop: 277, mobile: 222 }, { date: "2024-04-18", desktop: 311, mobile: 242 }, { date: "2024-04-19", desktop: 185, mobile: 242 }, { date: "2024-04-20", desktop: 247, mobile: 210 }, { date: "2024-04-21", desktop: 194, mobile: 253 }, { date: "2024-04-22", desktop: 159, mobile: 243 }, { date: "2024-04-23", desktop: 222, mobile: 190 }, { date: "2024-04-24", desktop: 237, mobile: 227 }, { date: "2024-04-25", desktop: 235, mobile: 160 }, { date: "2024-04-26", desktop: 211, mobile: 180 }, { date: "2024-04-27", desktop: 192, mobile: 143 }, { date: "2024-04-28", desktop: 235, mobile: 206 }, { date: "2024-04-29", desktop: 234, mobile: 223 }, { date: "2024-04-30", desktop: 212, mobile: 196 }, { date: "2024-05-01", desktop: 210, mobile: 270 }, { date: "2024-05-02", desktop: 283, mobile: 224 }, { date: "2024-05-03", desktop: 249, mobile: 254 }, { date: "2024-05-04", desktop: 238, mobile: 174 }, { date: "2024-05-05", desktop: 223, mobile: 205 }, { date: "2024-05-06", desktop: 250, mobile: 260 }, { date: "2024-05-07", desktop: 306, mobile: 345 }, { date: "2024-05-08", desktop: 287, mobile: 243 }, { date: "2024-05-09", desktop: 288, mobile: 326 }, { date: "2024-05-10", desktop: 371, mobile: 327 }, { date: "2024-05-11", desktop: 320, mobile: 323 }, { date: "2024-05-12", desktop: 354, mobile: 301 }, { date: "2024-05-13", desktop: 378, mobile: 374 }, { date: "2024-05-14", desktop: 386, mobile: 383 }, { date: "2024-05-15", desktop: 310, mobile: 375 }, { date: "2024-05-16", desktop: 365, mobile: 290 }, { date: "2024-05-17", desktop: 373, mobile: 358 }, { date: "2024-05-18", desktop: 347, mobile: 326 }, { date: "2024-05-19", desktop: 350, mobile: 264 }, { date: "2024-05-20", desktop: 276, mobile: 266 }, { date: "2024-05-21", desktop: 347, mobile: 219 }, { date: "2024-05-22", desktop: 231, mobile: 371 }, { date: "2024-05-23", desktop: 240, mobile: 310 }, { date: "2024-05-24", desktop: 253, mobile: 342 }, { date: "2024-05-25", desktop: 231, mobile: 255 }, { date: "2024-05-26", desktop: 190, mobile: 223 }, { date: "2024-05-27", desktop: 214, mobile: 272 }, { date: "2024-05-28", desktop: 285, mobile: 194 }, { date: "2024-05-29", desktop: 181, mobile: 235 }, { date: "2024-05-30", desktop: 223, mobile: 210 }, { date: "2024-05-31", desktop: 171, mobile: 155 }, { date: "2024-06-01", desktop: 244, mobile: 249 }, { date: "2024-06-02", desktop: 225, mobile: 252 }, { date: "2024-06-03", desktop: 181, mobile: 170 }, { date: "2024-06-04", desktop: 188, mobile: 198 }, { date: "2024-06-05", desktop: 177, mobile: 209 }, { date: "2024-06-06", desktop: 155, mobile: 207 }, { date: "2024-06-07", desktop: 171, mobile: 163 }, { date: "2024-06-08", desktop: 244, mobile: 232 }, { date: "2024-06-09", desktop: 208, mobile: 217 }, { date: "2024-06-10", desktop: 185, mobile: 293 }, { date: "2024-06-11", desktop: 233, mobile: 272 }, { date: "2024-06-12", desktop: 252, mobile: 336 }, { date: "2024-06-13", desktop: 334, mobile: 282 }, { date: "2024-06-14", desktop: 329, mobile: 265 }, { date: "2024-06-15", desktop: 342, mobile: 260 }, { date: "2024-06-16", desktop: 333, mobile: 339 }, { date: "2024-06-17", desktop: 385, mobile: 328 }, { date: "2024-06-18", desktop: 345, mobile: 266 }, { date: "2024-06-19", desktop: 321, mobile: 362 }, { date: "2024-06-20", desktop: 336, mobile: 272 }, { date: "2024-06-21", desktop: 289, mobile: 342 }, { date: "2024-06-22", desktop: 306, mobile: 340 }, { date: "2024-06-23", desktop: 296, mobile: 286 }, { date: "2024-06-24", desktop: 287, mobile: 269 }, { date: "2024-06-25", desktop: 353, mobile: 313 }, { date: "2024-06-26", desktop: 290, mobile: 291 }, { date: "2024-06-27", desktop: 305, mobile: 325 }, { date: "2024-06-28", desktop: 380, mobile: 381 }, { date: "2024-06-29", desktop: 302, mobile: 261 }, { date: "2024-06-30", desktop: 279, mobile: 297 }, ];
const config = { views: { label: "Page Views", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;
const labels = { desktop: "Desktop", mobile: "Mobile" } as const; const keys = ["desktop", "mobile"] as const; const totals = { desktop: daily.reduce((sum, d) => sum + d.desktop, 0), mobile: daily.reduce((sum, d) => sum + d.mobile, 0), };
let active = $state<"desktop" | "mobile">("desktop"); const data = $derived(daily.map((d) => ({ date: d.date, views: d[active] }))); const ticks = daily.filter((_, i) => i % 5 === 4).map((d) => d.date);
const formatDate = (v: string) => new Date(v).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });</script>
<Card.Root class="w-full max-w-4xl gap-0 py-0"> <div class="flex flex-col items-stretch border-b sm:flex-row"> <div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5"> <Card.Title class="font-semibold text-base tracking-[-0.2px]">Bar Chart - Interactive</Card.Title> <Card.Description class="text-[13.5px]">Showing total visitors for the last 3 months</Card.Description> </div> <div class="flex"> {#each keys as key (key)} <Chart.StatWell active={active === key} onclick={() => (active = key)}> {labels[key]} <b>{totals[key].toLocaleString()}</b> </Chart.StatWell> {/each} </div> </div> <Card.Content class="px-5 pt-5 pb-5"> <Chart.Container {config} class="aspect-auto h-60 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="date" series={[{ key: "views", label: config.views.label, color: "var(--color-views)" }]} bandPadding={0.2} grid={{ x: false }} props={{ bars: { radius: 2, rounded: "top" }, xAxis: { format: formatDate, ticks }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" labelFormatter={formatDate} /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>import { Card, CardContent, CardDescription, CardFooter, 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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} 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]"> Bar Chart </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-48 w-full"> <BarChart data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <ChartTooltip cursor={false} content={ <ChartTooltipContent hideLabel={false} indicator="dashed" /> } /> <Bar dataKey="desktop" fill="var(--color-desktop)" radius={[4, 4, 0, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dashed", labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-48 w-full" > <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ top: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisGroupedBar :x="x" :y="(d: Row) => d.desktop" :color="config.desktop.color" :rounded-corners="4" :group-max-width="24" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="(d: Row) => d.desktop" :template="template" :color="() => config.desktop.color" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-48 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="month" series={[{ key: "desktop", label: config.desktop.label, color: "var(--color-desktop)" }]} bandPadding={0.5} grid={{ x: false }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dashed" /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>Horizontal
Section titled “Horizontal”import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, XAxis, YAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} 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]"> Bar Chart - Horizontal </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-48 w-full"> <BarChart data={data} layout="vertical"> <YAxis dataKey="month" type="category" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <XAxis dataKey="desktop" type="number" hide /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="desktop" fill="var(--color-desktop)" radius={[0, 4, 4, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { GroupedBar } from "@unovis/ts";import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => data.length - 1 - i;const tickFormat = (i: number) => data[data.length - 1 - i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { labelKey: "month", indicator: "dot",});const triggers = { [GroupedBar.selectors.bar]: template };</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]">Bar Chart - Horizontal</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-48 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ left: 4 }"> <VisGroupedBar :x="x" :y="(d: Row) => d.desktop" :color="config.desktop.color" orientation="horizontal" :rounded-corners="4" :group-max-width="24" /> <VisAxis type="y" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip :triggers="triggers" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart - Horizontal</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-48 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} y="month" orientation="horizontal" series={[{ key: "desktop", label: config.desktop.label, color: "var(--color-desktop)" }]} bandPadding={0.35} grid={false} axis="y" props={{ bars: { radius: 4, rounded: "right", height: 24 }, yAxis: { format: (v: string) => v.slice(0, 3) }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>Multiple
Section titled “Multiple”import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", 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]"> Bar Chart - Multiple </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-52 w-full"> <BarChart data={data} barGap={2}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <ChartTooltip cursor={false} content={<ChartTooltipContent indicator="dashed" />} /> <ChartLegend content={<ChartLegendContent />} /> <Bar dataKey="desktop" fill="var(--color-desktop)" radius={[4, 4, 0, 0]} maxBarSize={24} /> <Bar dataKey="mobile" fill="var(--color-mobile)" radius={[4, 4, 0, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartLegendContent, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = [config.desktop.color, config.mobile.color];const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dashed", labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart - Multiple</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-52 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ top: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisGroupedBar :x="x" :y="[(d: Row) => d.desktop, (d: Row) => d.mobile]" :color="(_: Row, i: number) => colors[i]" :rounded-corners="4" :group-max-width="48" :bar-padding="0.05" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.desktop, (d: Row) => d.mobile]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> <ChartLegendContent /> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart - Multiple</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-52 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="month" legend seriesLayout="group" groupPadding={0.1} series={[ { key: "desktop", label: config.desktop.label, color: "var(--color-desktop)" }, { key: "mobile", label: config.mobile.label, color: "var(--color-mobile)" }, ]} bandPadding={0.3} grid={{ x: false }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dashed" /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>Stacked + Legend
Section titled “Stacked + Legend”import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", 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]"> Bar Chart - Stacked + Legend </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-52 w-full"> <BarChart data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <ChartTooltip content={<ChartTooltipContent hideLabel={false} />} /> <ChartLegend content={<ChartLegendContent />} /> <Bar dataKey="desktop" stackId="a" fill="var(--color-desktop)" stroke="var(--card)" strokeWidth={2} radius={0} maxBarSize={24} /> <Bar dataKey="mobile" stackId="a" fill="var(--color-mobile)" stroke="var(--card)" strokeWidth={2} radius={[4, 4, 0, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> </Card> );}<script setup lang="ts">import { VisAxis, VisStackedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartLegendContent, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-2)" },} satisfies ChartConfig;
const colors = [config.desktop.color, config.mobile.color];const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dot", labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart - Stacked + Legend</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-[15.7rem] w-full [&_[data-vis-xy-container]]:!h-52 [&_[data-vis-xy-container]_.bar]:stroke-card [&_[data-vis-xy-container]_.bar]:stroke-2"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ top: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisStackedBar :x="x" :y="[(d: Row) => d.desktop, (d: Row) => d.mobile]" :color="(_: Row, i: number) => colors[i]" :rounded-corners="4" :bar-max-width="24" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="[(d: Row) => d.desktop, (d: Row) => d.mobile]" :template="template" :color="(_: Row, i: number) => colors[i]" /> </VisXYContainer> <ChartLegendContent /> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart - Stacked + Legend</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-52 w-full [&_.lc-bar]:stroke-card [&_.lc-bar]:stroke-2"> <BarChart {data} x="month" legend seriesLayout="stack" stackPadding={2} series={[ { key: "desktop", label: config.desktop.label, color: "var(--color-desktop)", props: { rounded: "none" } }, { key: "mobile", label: config.mobile.label, color: "var(--color-mobile)", props: { rounded: "top" } }, ]} bandPadding={0.5} grid={{ x: false }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" /> {/snippet} </BarChart> </Chart.Container> </Card.Content></Card.Root>import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} 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]"> Bar Chart - Label </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-52 w-full"> <BarChart data={data} margin={{ top: 20 }}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="desktop" fill="var(--color-desktop)" radius={[4, 4, 0, 0]} maxBarSize={24} > <LabelList position="top" offset={8} className="fill-foreground" fontSize={12} /> </Bar> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { VisAxis, VisGroupedBar, VisXYContainer, VisXYLabels } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dot", hideLabel: true, labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart - Label</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-52 w-full"> <VisXYContainer :data="data" :y-domain="[0, 340]" :margin="{ top: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisGroupedBar :x="x" :y="(d: Row) => d.desktop" :color="config.desktop.color" :rounded-corners="4" :group-max-width="24" /> <VisXYLabels :x="(d: Row) => data.findIndex((r) => r.month === d.month)" :y="(d: Row) => d.desktop + 30" :label="(d: Row) => String(d.desktop)" :label-font-size="12" :clustering="false" background-color="transparent" :color="() => 'var(--foreground)'" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="(d: Row) => d.desktop" :template="template" :color="() => config.desktop.color" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart - Label</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-52 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="month" series={[{ key: "desktop", label: config.desktop.label, color: "var(--color-desktop)" }]} bandPadding={0.5} labels={{ offset: 8 }} grid={{ x: false }} padding={{ top: 20, left: 0, right: 0, bottom: 24 }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, Cell, XAxis, YAxis } from "recharts";
const data = [ { browser: "chrome", visitors: 275 }, { browser: "safari", visitors: 200 }, { browser: "firefox", visitors: 187 }, { browser: "edge", visitors: 173 }, { browser: "other", visitors: 90 },];const config = { visitors: { label: "Visitors" }, chrome: { label: "Chrome", color: "var(--chart-1)" }, safari: { label: "Safari", color: "var(--chart-2)" }, firefox: { label: "Firefox", color: "var(--chart-3)" }, edge: { label: "Edge", color: "var(--chart-4)" }, other: { label: "Other", color: "var(--chart-5)" },} 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]"> Bar Chart - Mixed </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-48 w-full"> <BarChart data={data} layout="vertical" margin={{ left: 0 }}> <YAxis dataKey="browser" type="category" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => config[value as keyof typeof config]?.label ?? value } /> <XAxis dataKey="visitors" type="number" hide /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="visitors" radius={[0, 4, 4, 0]} maxBarSize={24}> {data.map((d) => ( <Cell key={d.browser} fill={`var(--color-${d.browser})`} /> ))} </Bar> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { GroupedBar } from "@unovis/ts";import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { browser: string; visitors: number } & Record<string, string | number>;
const data: Row[] = [ { browser: "chrome", visitors: 275, chrome: 275 }, { browser: "safari", visitors: 200, safari: 200 }, { browser: "firefox", visitors: 187, firefox: 187 }, { browser: "edge", visitors: 173, edge: 173 }, { browser: "other", visitors: 90, other: 90 },];
const config = { chrome: { label: "Chrome", color: "var(--chart-1)" }, safari: { label: "Safari", color: "var(--chart-2)" }, firefox: { label: "Firefox", color: "var(--chart-3)" }, edge: { label: "Edge", color: "var(--chart-4)" }, other: { label: "Other", color: "var(--chart-5)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => data.length - 1 - i;const tickFormat = (i: number) => config[data[data.length - 1 - i]?.browser as keyof typeof config]?.label ?? "";const colorOf = (d: Row) => config[d.browser as keyof typeof config].color;const template = componentToString(config, ChartTooltipContent, { hideLabel: true, indicator: "dot",});const triggers = { [GroupedBar.selectors.bar]: template };</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]">Bar Chart - Mixed</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-48 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ left: 4 }"> <VisGroupedBar :x="x" :y="(d: Row) => d.visitors" :color="colorOf" orientation="horizontal" :rounded-corners="4" :group-max-width="24" /> <VisAxis type="y" :x="x" :tick-format="tickFormat" :num-ticks="5" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip :triggers="triggers" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { browser: "chrome", visitors: 275 }, { browser: "safari", visitors: 200 }, { browser: "firefox", visitors: 187 }, { browser: "edge", visitors: 173 }, { browser: "other", visitors: 90 }, ]; const config = { chrome: { label: "Chrome", color: "var(--chart-1)" }, safari: { label: "Safari", color: "var(--chart-2)" }, firefox: { label: "Firefox", color: "var(--chart-3)" }, edge: { label: "Edge", color: "var(--chart-4)" }, other: { label: "Other", color: "var(--chart-5)" }, } satisfies Chart.ChartConfig; const keys = ["chrome", "safari", "firefox", "edge", "other"] as const; const label = (k: string) => config[k as keyof typeof config]?.label ?? k;</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]">Bar Chart - Mixed</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-48 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} y="browser" x="visitors" orientation="horizontal" c="browser" cDomain={[...keys]} cRange={keys.map((k) => config[k].color)} bandPadding={0.35} grid={false} axis="y" props={{ bars: { radius: 4, rounded: "right", height: 24 }, yAxis: { format: label }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel nameKey="browser" /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>Active
Section titled “Active”import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, Cell, XAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
// The active bar keeps the series colour, the others drop to 45% (identity of one).const activeIndex = 2;
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]"> Bar Chart - Active </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-48 w-full"> <BarChart data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="desktop" radius={[4, 4, 0, 0]} maxBarSize={24}> {data.map((d, i) => ( <Cell key={d.month} fill="var(--color-desktop)" fillOpacity={i === activeIndex ? 1 : 0.45} /> ))} </Bar> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
// The active bar keeps the series colour, the others drop to 45% (identity of one).const activeIndex = 2;const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dot", hideLabel: true, labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart - Active</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-48 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ top: 4 }"> <VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="() => ''" /> <VisGroupedBar :x="x" :y="(d: Row) => d.desktop" :color="(_: Row, i: number) => i === activeIndex ? config.desktop.color : 'color-mix(in srgb, var(--chart-1) 45%, var(--card))'" :rounded-corners="4" :group-max-width="24" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="(d: Row) => d.desktop" :template="template" :color="() => config.desktop.color" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;
// The active bar keeps the series colour, the others drop to 45% (identity of one). const activeIndex = 2; const faded = "color-mix(in srgb, var(--chart-1) 45%, var(--card))";</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]">Bar Chart - Active</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-48 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="month" y="desktop" c="month" cDomain={data.map((d) => d.month)} cRange={data.map((_, i) => (i === activeIndex ? config.desktop.color : faded))} bandPadding={0.5} grid={{ x: false }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { format: () => "" }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>Custom Label
Section titled “Custom Label”import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, LabelList, XAxis, YAxis } from "recharts";
const data = [ { browser: "chrome", visitors: 275 }, { browser: "safari", visitors: 200 }, { browser: "firefox", visitors: 187 }, { browser: "edge", visitors: 173 }, { browser: "other", visitors: 90 },];
const config = { visitors: { label: "Visitors", color: "var(--chart-1)" }, chrome: { label: "Chrome" }, safari: { label: "Safari" }, firefox: { label: "Firefox" }, edge: { label: "Edge" }, other: { label: "Other" },} 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]"> Bar Chart - Custom Label </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-52 w-full"> <BarChart data={data} layout="vertical" margin={{ right: 28 }}> <YAxis dataKey="browser" type="category" tickLine={false} tickMargin={10} axisLine={false} width={56} tickFormatter={(value) => config[value as keyof typeof config]?.label ?? value } /> <XAxis dataKey="visitors" type="number" hide /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="visitors" fill="var(--color-visitors)" radius={[0, 4, 4, 0]} maxBarSize={24} > <LabelList dataKey="visitors" position="right" offset={8} className="fill-foreground" fontSize={12} /> </Bar> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { GroupedBar } from "@unovis/ts";import { VisAxis, VisGroupedBar, VisXYContainer, VisXYLabels } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { browser: string; visitors: number } & Record<string, string | number>;
const data: Row[] = [ { browser: "chrome", visitors: 275, chrome: 275 }, { browser: "safari", visitors: 200, safari: 200 }, { browser: "firefox", visitors: 187, firefox: 187 }, { browser: "edge", visitors: 173, edge: 173 }, { browser: "other", visitors: 90, other: 90 },];
const config = { chrome: { label: "Chrome", color: "var(--chart-1)" }, safari: { label: "Safari", color: "var(--chart-2)" }, firefox: { label: "Firefox", color: "var(--chart-3)" }, edge: { label: "Edge", color: "var(--chart-4)" }, other: { label: "Other", color: "var(--chart-5)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => data.length - 1 - i;const tickFormat = (i: number) => config[data[data.length - 1 - i]?.browser as keyof typeof config]?.label ?? "";const colorOf = () => "var(--chart-1)";const template = componentToString(config, ChartTooltipContent, { hideLabel: true, indicator: "dot",});const triggers = { [GroupedBar.selectors.bar]: template };</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]">Bar Chart - Custom Label</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-52 w-full"> <VisXYContainer :data="data" :y-domain="[0, undefined]" :margin="{ left: 4, right: 28 }"> <VisGroupedBar :x="x" :y="(d: Row) => d.visitors" :color="colorOf" orientation="horizontal" :rounded-corners="4" :group-max-width="24" /> <VisXYLabels :x="(d: Row) => d.visitors + 18" :y="(d: Row) => data.length - 1 - data.findIndex((r) => r.browser === d.browser)" :label="(d: Row) => String(d.visitors)" :label-font-size="12" :clustering="false" background-color="transparent" :color="() => 'var(--foreground)'" /> <VisAxis type="y" :x="x" :tick-format="tickFormat" :num-ticks="5" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip :triggers="triggers" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { browser: "chrome", visitors: 275 }, { browser: "safari", visitors: 200 }, { browser: "firefox", visitors: 187 }, { browser: "edge", visitors: 173 }, { browser: "other", visitors: 90 }, ]; const config = { chrome: { label: "Chrome", color: "var(--chart-1)" }, safari: { label: "Safari", color: "var(--chart-2)" }, firefox: { label: "Firefox", color: "var(--chart-3)" }, edge: { label: "Edge", color: "var(--chart-4)" }, other: { label: "Other", color: "var(--chart-5)" }, } satisfies Chart.ChartConfig; const keys = ["chrome", "safari", "firefox", "edge", "other"] as const; const label = (k: string) => config[k as keyof typeof config]?.label ?? k;</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]">Bar Chart - Custom Label</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-52 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} y="browser" x="visitors" orientation="horizontal" series={[{ key: "visitors", label: "Visitors", color: "var(--chart-1)" }]} bandPadding={0.35} labels={{ offset: 8 }} grid={false} axis="y" padding={{ top: 4, left: 50, right: 32, bottom: 4 }} props={{ bars: { radius: 4, rounded: "right", height: 24 }, yAxis: { format: label }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel nameKey="browser" /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart";import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
const data = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} 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]"> Bar Chart - Axes </CardTitle> <CardDescription className="text-[13.5px]"> January - June 2024 </CardDescription> </CardHeader> <CardContent className="items-center px-5 pt-2.5 pb-0"> <ChartContainer config={config} className="aspect-auto h-52 w-full"> <BarChart data={data}> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} tickFormatter={(value) => value.slice(0, 3)} /> <YAxis tickLine={false} axisLine={false} tickMargin={8} width={32} domain={[0, 400]} ticks={[0, 133, 266, 400]} /> <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} /> <Bar dataKey="desktop" fill="var(--color-desktop)" radius={[4, 4, 0, 0]} maxBarSize={24} /> </BarChart> </ChartContainer> </CardContent> <CardFooter className="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b className="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d="m22 7-8.5 8.5-5-5L2 17" /> <path d="M16 7h6v6" /> </svg> </b> <span className="text-muted-foreground"> Showing total visitors for the last 6 months </span> </CardFooter> </Card> );}<script setup lang="ts">import { VisAxis, VisGroupedBar, VisXYContainer } from "@unovis/vue";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card";import { type ChartConfig, ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString,} from "@/components/ui/chart";
type Row = { month: string; desktop: number; mobile: number };
const data: Row[] = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 },];
const config = { desktop: { label: "Desktop", color: "var(--chart-1)" },} satisfies ChartConfig;
const x = (_: Row, i: number) => i;const tickFormat = (i: number) => data[i]?.month.slice(0, 3) ?? "";const template = componentToString(config, ChartTooltipContent, { indicator: "dot", hideLabel: true, labelFormatter: (i) => data[Number(i)]?.month ?? "",});</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]">Bar Chart - Axes</CardTitle> <CardDescription class="text-[13.5px]">January - June 2024</CardDescription> </CardHeader> <CardContent class="items-center px-5 pt-2.5 pb-0"> <ChartContainer :config="config" class="aspect-auto h-52 w-full"> <VisXYContainer :data="data" :y-domain="[0, 400]" :margin="{ top: 8 }"> <VisAxis type="y" :tick-values="[0, 133, 266, 400]" :tick-line="false" :domain-line="false" /> <VisGroupedBar :x="x" :y="(d: Row) => d.desktop" :color="config.desktop.color" :rounded-corners="4" :group-max-width="24" /> <VisAxis type="x" :x="x" :tick-format="tickFormat" :num-ticks="6" :tick-line="false" :domain-line="false" :grid-line="false" /> <ChartTooltip /> <ChartCrosshair :x="x" :y="(d: Row) => d.desktop" :template="template" :color="() => config.desktop.color" /> </VisXYContainer> </ChartContainer> </CardContent> <CardFooter class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </CardFooter> </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 = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const config = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies Chart.ChartConfig;</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]">Bar Chart - Axes</Card.Title> <Card.Description class="text-[13.5px]">January - June 2024</Card.Description> </Card.Header> <Card.Content class="items-center px-5 pt-2.5 pb-0"> <Chart.Container {config} class="aspect-auto h-52 w-full [&_.lc-bar]:stroke-none"> <BarChart {data} x="month" series={[{ key: "desktop", label: config.desktop.label, color: "var(--color-desktop)" }]} yDomain={[0, 400]} bandPadding={0.5} grid={{ x: false }} props={{ bars: { radius: 4, rounded: "top", width: 24 }, xAxis: { format: (v: string) => v.slice(0, 3) }, yAxis: { ticks: [0, 133, 266, 400] }, }} > {#snippet tooltip()} <Chart.Tooltip indicator="dot" hideLabel /> {/snippet} </BarChart> </Chart.Container> </Card.Content> <Card.Footer class="flex-col items-stretch gap-1 px-6 pt-3.5 pb-5.5 text-[13.5px]"> <b class="inline-flex items-center gap-2 font-medium"> Trending up by 5.2% this month <svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></svg> </b> <span class="text-muted-foreground">Showing total visitors for the last 6 months</span> </Card.Footer></Card.Root>