- accordion
- alert dialog
- annonce
- aspect ratio
- avatar
- badge
- banner
- button
- calendar
- card
- checkbox
- context menu
- date picker
- dialog
- drawer
- dropdown menu
- empty state
- hover card
- input
- label
- popover
- progress
- radio group
- scroll area
- select
- separator
- slider
- switch
- tabs
- textarea
- toast
- toggle group
- toggle
- toolbar
- tooltip
Patterns
Patterns are a way to add visual interest to your charts.
Installation
Copy-paste the component code in a file named Patterns.tsx
in your tailus-ui/visualizations folder.
export const Stripes = ({ intent = 'primary', id }: { intent?: string; id: string }) => {
return (
<pattern id={id} patternUnits="userSpaceOnUse" width="4" height="4">
<rect width="4" height="4" fill={`var(--dv-${intent})`} opacity={0.3} />
<path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" stroke={`var(--dv-${intent})`} opacity={0.6} strokeWidth="1" />
</pattern>
);
};
export const Dots = ({ intent = 'primary', id }: { intent?: string; id: string }) => {
return (
<pattern id={id} patternUnits="userSpaceOnUse" width="8" height="8">
<circle cx="2" cy="2" r="2" fill={`var(--dv-${intent})`} />
</pattern>
);
};
export const LinearGradient = ({ intent = 'primary', id }: { intent?: string; id: string }) => {
return (
<linearGradient x1={0} y1={0} x2={0} y2={1} id={id}>
<stop offset="5%" stopColor={`var(--dv-${intent})`} stopOpacity={0.3} />
<stop className="text-white dark:text-gray-950" offset="95%" stopColor="currentColor" stopOpacity={0} />
</linearGradient>
);
};
export const DotCartesianGrid = ({ id, opacity = 0.5 }: { id: string; opacity: number }) => {
return (
<pattern id={id} patternUnits="userSpaceOnUse" width="8" height="8">
<circle cx="2" cy="2" r="1" opacity={opacity} fill="var(--ui-border-color)" />
</pattern>
);
};
Usage
Import the Pattern of your choice from your local files and use it inside the <defs>
tag in your Chart component.
import { LinearGradient, Dot, Stripes, DotCartesianGrid } from '@tailus-ui/visualizations';
<defs>
<LinearGradient intent="accent" id="Accent" />
<LinearGradient intent="primary" id="Primary" />
<Dot intent="primary-600" id="dP600"/>
<Stripes intent="accent-500" id="sA500"/>
<DotCartesianGrid id="dCart" opacity={0.6}/>
</defs>
Assign a unique identifier to the Pattern component using the id
prop, for example id="Gray"
. Then, use this id
as the value for the fill
prop of the Chart componen, like so: fill="url(#Gray)"
. This links the pattern with the id="Gray"
to the Chart component it should be applied to.
<Area stroke="var(--dv-accent)" fill="url(#Accent)" dataKey="Accent" />
<Bar fill="url(#sA500)" dataKey="Accent" />
<CartesianGrid fill="url(#grid)" vertical={false} horizontal={false} />
Reference
Dots, Stripes, and Linear Gradients
These patterns has the following props
intent
string
primary
id*
string
opacity
number
The intent prop accepts a string
, so primary
and primary-600
are valid values
DotCartesianGrid
The DotCartesianGrid component has the following props
opacity
number
0.5
id*
string
Stripes
Visitors and Revenue
New users by First user primary channel group (Default Channel Group)
import { ResponsiveContainer, BarChart, Bar, Tooltip, YAxis, XAxis, CartesianGrid } from 'recharts';
import { CustomTooltip, Stripes, data } from "@tailus-ui/visualizations";
import { Text, Title } from '@tailus-ui/typography';
export const MyChart = () => {
return (
<div className="w-full">
<Title as="h2" size="lg" weight="medium">
Visitors and Revenue
</Title>
<Text className="mb-8 mt-2" size="sm">
New users by First user primary channel group (Default Channel Group)
</Text>
<div className="h-56 w-full sm:h-72 sm:max-w-xl">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data}>
<YAxis className="text-caption" width={40} fontSize={12} unit="$" width={42} tickLine={false} axisLine={false} />
<XAxis className="text-caption" dataKey="name" fontSize={12} tickLine={false} axisLine={false} />
<CartesianGrid vertical={false} stroke="var(--ui-border-color)" strokeDasharray={3} />
<Tooltip cursor={{ fill: 'var(--ui-border-color)' }} content={<CustomTooltip payload={[]} active mixed label={''} />} />
<Bar fill="var(--dv-primary)" stackId="a" barSize={20} dataKey="Primary" />
<Bar fill="var(--dv-accent)" stackId="a" dataKey="Accent" barSize={20} />
<Bar radius={[4, 4, 0, 0]} fill="url(#strbar)" stackId="a" dataKey="Neutral" barSize={20} />
<defs>
<Stripes id="strbar" intent="primary-500" />
</defs>
</BarChart>
</ResponsiveContainer>
</div>
</div>
)
}
Linear Gradient
import { ResponsiveContainer, BarChart, Bar, Tooltip, YAxis, XAxis } from 'recharts';
import { CustomTooltip, LinearGradient, barData } from "@tailus-ui/visualizations";
import { Text, Title } from '@tailus-ui/typography';
export const MyChart = () => {
return (
<div className="h-56 w-full pt-4 sm:h-72 sm:pt-0" data-shade="900">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={barData}>
<defs>
<LinearGradient intent="accent" id="Accent" />
<LinearGradient intent="primary" id="Primary" />
</defs>
<Area type="natural" stroke="var(--dv-accent)" fill="url(#Accent)" dataKey="Neutral" activeDot={{ color: 'var(--dv-accent)', r: 4, stroke: 'none' }} />
<Area type="natural" stroke="var(--dv-primary)" fill="url(#Primary)" dataKey="Primary" activeDot={{ color: 'var(--dv-primary)', r: 4, stroke: 'none' }} />
</AreaChart>
</ResponsiveContainer>
</div>
)
}
Dot Cartesian Grid
Overview
Visualize your main activities data
185,267,931.00 36%import { useEffect, useState } from 'react';
import { ResponsiveContainer, LineChart, Tooltip, YAxis, XAxis, Line, CartesianGrid } from 'recharts';
import { CustomTooltip } from "@tailus-ui/visualizations";
import { Text, Title } from '@tailus-ui/typography';
import Badge from '@tailus-ui/Badge';
import { TrendingDown, TrendingUp } from 'lucide-react';
export const CryptoLineChart = () => {
const [bitcoinData, setBitcoinData] = useState([]);
useEffect(() => {
const endTimestamp = Date.now();
const startTimestamp = endTimestamp - 6 * 30 * 24 * 60 * 60 * 1000;
fetch(`https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range?vs_currency=usd&from=${startTimestamp / 1000}&to=${endTimestamp / 1000}`)
.then((response) => response.json())
.then((data) => {
const prices = data.prices.map((price) => ({
date: new Date(price[0]).toISOString().slice(0, 10),
price: price[1]
}));
setBitcoinData(prices);
})
.catch((error) => console.error(error));
}, []);
return (
<div className="w-full">
<Title as="h2" size="lg" weight="medium" className="mb-1">
Overview
</Title>
<Text className="mb-0 mt-1" size="sm">
Visualize your main activities data
</Text>
<Title className="mt-2 flex items-center gap-3" as="span" weight="medium" size="xl">
185,267,931.00 <span className="text-caption -ml-2 mb-1 mt-auto h-fit text-xs">CDF</span>
<Badge intent="success" size="xs" className="flex h-fit items-center gap-1.5">
<TrendingUp className="size-3.5" />
36%
</Badge>
</Title>
<div className="mt-8 h-56 w-full sm:h-72" data-shade="900">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={bitcoinData}>
<YAxis className="text-caption" width={42} fontSize={11} tickLine={false} axisLine={false} />
<XAxis className="text-caption" dataKey="date" fontSize={12} tickLine={false} axisLine={false} />
<Tooltip active coordinate={{ x: 100 }} cursor={{ stroke: 'var(--ui-border-color)', strokeWidth: 1 }} position={{ y: 0 }} offset={20} content={<Custom payload={[]} active fancy label={''} />} />
<CartesianGrid vertical={false} horizontal={false} fill="url(#grid)" stroke="var(--ui-border-color)" />
<Line
type="natural"
stroke="var(--dv-primary)"
strokeWidth={1.5}
dataKey="price"
dot={false}
unit="CDF"
activeDot={{
color: 'var(--dv-primary)',
r: 3,
stroke: 'currentColor'
}}
/>
<defs>
<pattern id="grid" patternUnits="userSpaceOnUse" width="8" height="8">
<circle cx="2" cy="2" r="1" opacity={0.4} fill="var(--ui-border-color)" />
</pattern>
</defs>
</LineChart>
</ResponsiveContainer>
</div>
<div></div>
</div>
);
};
Dots
import { ResponsiveContainer, Legend, PieChart, Pie, Cell } from 'recharts';
import { Dots, legendText } from '@tailus-ui/visualizations';
const data = [
{
name: 'Running',
uv: 31.47,
pv: 2400,
fill: 'url(#dotP)'
},
{
name: 'Swimming',
uv: 26.69,
pv: 4567,
fill: 'url(#dotA)'
},
{
name: 'Cycling',
uv: 15.69,
pv: 1398,
fill: 'url(#dotS)'
},
{
name: 'Weightlifting',
uv: 8.22,
pv: 9800,
fill: 'url(#dotS)'
},
{
name: 'Yoga',
uv: 8.63,
pv: 3908,
fill: 'url(#dotI)'
},
{
name: 'Pilates',
uv: 2.63,
pv: 4800,
fill: 'url(#dotW)'
},
{
name: 'Boxing',
uv: 6.67,
pv: 4800,
fill: 'url(#dotD)'
}
];
export const CategoryBar = () => {
return (
<div className="mx-auto w-96">
<ResponsiveContainer aspect={5 / 4}>
<PieChart>
<Pie label={{ fontSize: 12, fill: 'var(--title-text-color)' }} startAngle={180} endAngle={0} data={data} cx={180} cy={200} innerRadius={70} outerRadius={140} fill="var(--area-primary-stroke)" dataKey="uv">
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.fill} stroke="none" />
))}
</Pie>
<Legend formatter={legendText} iconType="circle" iconSize={9} />
<defs>
<Dots id="dotP" />
<Dots intent="accent" id="dotA" />
<Dots intent="secondary" id="dotS" />
<Dots intent="warning" id="dotW" />
<Dots intent="danger" id="dotD" />
<Dots intent="info" id="dotI" />
</defs>
</PieChart>
</ResponsiveContainer>
</div>
);
};