| name | health-charts |
| description | Generate and configure Chart.js health data visualizations including severity timelines, frequency bar charts, trigger correlation charts, and calendar heatmaps. Use when building or modifying charts in any healthcare or wellness tracking app. |
health-charts
Chart.js patterns and configurations for health tracking data visualizations.
When to use
- Building a severity-over-time line chart
- Creating a symptom frequency bar chart
- Rendering a trigger correlation horizontal bar chart
- Building a calendar heatmap for symptom intensity
- Displaying a radar chart for body-part or time-of-day distribution
Chart.js global health config
Apply these global defaults in your app initialization to match the health theme:
import { Chart, defaults } from 'chart.js';
defaults.font.family = "'Instrument Sans', system-ui, sans-serif";
defaults.font.size = 12;
defaults.color = '#57534e';
defaults.borderColor = '#e7e5e4';
defaults.plugins.legend.labels.usePointStyle = true;
defaults.plugins.legend.labels.pointStyleWidth = 10;
defaults.plugins.tooltip.backgroundColor = '#ffffff';
defaults.plugins.tooltip.borderColor = '#e7e5e4';
defaults.plugins.tooltip.borderWidth = 1;
defaults.plugins.tooltip.titleColor = '#1c1917';
defaults.plugins.tooltip.bodyColor = '#57534e';
defaults.plugins.tooltip.cornerRadius = 8;
defaults.plugins.tooltip.padding = 10;
Severity color helper
Map a numeric severity (1-10) to the appropriate CSS variable color:
function severityColor(value: number): string {
if (value <= 3) return '#16a34a';
if (value <= 6) return '#d97706';
return '#dc2626';
}
function severityBgColor(value: number): string {
if (value <= 3) return '#f0fdf4';
if (value <= 6) return '#fffbeb';
return '#fef2f2';
}
Severity timeline (line chart)
const config: ChartConfiguration = {
type: 'line',
data: {
labels: dates,
datasets: [{
label: 'Severity',
data: severities,
borderColor: '#0891b2',
backgroundColor: 'rgba(8, 145, 178, 0.1)',
borderWidth: 2.5,
pointRadius: 4,
pointBackgroundColor: severities.map(severityColor),
pointBorderColor: '#ffffff',
pointBorderWidth: 2,
tension: 0.3,
fill: true,
}, {
label: '7-day average',
data: rollingAvg,
borderColor: '#06b6d4',
borderWidth: 2,
borderDash: [5, 5],
pointRadius: 0,
fill: false,
tension: 0.4,
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
: {
: ,
: ,
: {
: ,
: ,
},
: { : },
},
: {
: ,
: { : , : { : } },
: { : },
},
},
: {
: {
: {
: ,
},
},
},
},
};
Frequency bar chart
const config: ChartConfiguration = {
type: 'bar',
data: {
labels: symptomNames,
datasets: [{
label: 'Occurrences',
data: counts,
backgroundColor: ['#0891b2', '#67e8f9', '#a5f3fc', '#cffafe', '#e0f9ff'],
borderRadius: 4,
borderSkipped: false,
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: { beginAtZero: true, grid: { color: '#e7e5e4' } },
x: { grid: { display: false } },
},
},
};
Trigger correlation (horizontal bar)
const config: ChartConfiguration = {
type: 'bar',
data: {
labels: triggerNames,
datasets: [{
label: 'Co-occurrence %',
data: rates,
backgroundColor: '#0891b2',
borderRadius: 4,
}],
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
min: 0, max: 100,
ticks: { callback: (v) => `${v}%` },
grid: { color: '#e7e5e4' },
},
y: { grid: { display: false } },
},
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (ctx) => `Present in ${ctx.parsed.x}% of entries`,
},
},
},
},
};
Calendar heatmap
Calendar heatmap is not natively supported by Chart.js. Use a custom canvas drawing approach or a library like chartjs-chart-matrix.
With chartjs-chart-matrix
pnpm add chartjs-chart-matrix
import { MatrixController, MatrixElement } from 'chartjs-chart-matrix';
Chart.register(MatrixController, MatrixElement);
const config = {
type: 'matrix' as const,
data: {
datasets: [{
label: 'Symptoms',
data: heatmapData,
backgroundColor: (ctx: ScriptableContext<'matrix'>) => {
const v = (ctx.dataset.data[ctx.dataIndex] as any).v as number;
if (!v) return '#e7e5e4';
if (v <= 3) return '#cffafe';
if (v <= 5) return '#67e8f9';
if (v <= 7) return '#0891b2';
return '#0e7490';
},
width: (ctx..?. / ) - ,
: (ctx..?. / ) - ,
: ,
}],
},
: {
: {
: {
: {
: (items[]..[items[].] ).,
: {
v = (ctx..[ctx.] ).;
v ? : ;
},
},
},
: { : },
},
: {
: { : },
: {
: ,
: [, , , , , , ],
: ,
: { : },
: ,
},
},
},
};
Time-of-day radar chart
const config: ChartConfiguration = {
type: 'radar',
data: {
labels: ['Morning', 'Afternoon', 'Evening', 'Night'],
datasets: [{
label: 'Occurrence %',
data: [60, 20, 15, 5],
backgroundColor: 'rgba(8, 145, 178, 0.15)',
borderColor: '#0891b2',
borderWidth: 2,
pointBackgroundColor: '#0891b2',
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
r: {
beginAtZero: true,
max: 100,
ticks: { callback: (v) => `${v}%`, stepSize: 25 },
grid: { color: '#e7e5e4' },
},
},
plugins: { legend: { display: false } },
},
};
Rolling average calculation
function rollingAverage(data: number[], window: number): (number | null)[] {
return data.map((_, i) => {
if (i < window - 1) return null;
const slice = data.slice(i - window + 1, i + 1).filter((v) => v !== null);
if (slice.length === 0) return null;
return slice.reduce((a, b) => a + b, 0) / slice.length;
});
}
Accessibility notes
- Always provide
aria-label on <canvas>: aria-label="Chart showing symptom severity over time"
- Add a
<table> with the same data below the chart for screen reader users (visually hidden with sr-only class)
- Do not rely on color alone to communicate severity: include numeric labels in tooltips