A line chart that turns red where it drops below zero, line and fill alike. The same trick works for any threshold, not just zero.
Coloring the part of a line beyond a threshold shows at a glance when a value was good or bad, without reading the axis. Zero is the natural line for profit and loss or temperatures; a target, a limit or an average works the same way.
Built with amCharts 5, a JavaScript library. The JavaScript includes your theme and renderer (canvas or SVG); the JSON also follows the changes you make with the controls.
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Chart Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root and chart
var root = am5.Root.new("chartdiv");
root.setThemes([am5themes_Animated.new(root), am5themes_Responsive.new(root)]);
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
wheelY: "zoomX" // the mouse wheel zooms in on the dates
})
);
// Generate random data: a slow wave with some noise on it,
// so the line dips below zero and comes back up
var data = generateChartData();
function generateChartData() {
var chartData = [];
var firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 150);
var noise = 0;
for (var i = 0; i < 150; i++) {
var newDate = new Date(firstDate);
newDate.setHours(0, 0, 0);
newDate.setDate(newDate.getDate() + i);
// random noise that wanders but keeps pulling back toward zero
noise = (noise + (Math.random() - 0.5) * 8) * 0.92;
chartData.push({
date: newDate.getTime(),
value: Math.round(Math.sin((i + 8) / 20) * 45 + 10 + noise) // a slow sine wave plus the noise
});
}
return chartData;
}
// Create Y-axis
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
// the axis tooltip shows one more decimal than the axis labels
extraTooltipPrecision: 1,
renderer: am5xy.AxisRendererY.new(root, {
minGridDistance: 30 // at least 30px between labels
})
})
);
// Create X-Axis
var xAxis = chart.xAxes.push(
am5xy.DateAxis.new(root, {
baseInterval: { timeUnit: "day", count: 1 }, // one point per day
renderer: am5xy.AxisRendererX.new(root, {
minorGridEnabled: true, // fainter grid lines between the labeled dates
cellStartLocation: 0.2,
cellEndLocation: 0.8
})
})
);
// Create series
var series = chart.series.push(
am5xy.SmoothedXLineSeries.new(root, {
xAxis: xAxis,
yAxis: yAxis,
valueYField: "value",
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
labelText:"{valueX.formatDate()}: {valueY}", // the date and the value
pointerOrientation:"horizontal" // the tooltip points sideways at the line
})
})
);
series.strokes.template.setAll({
strokeWidth: 3 // a 3px line
});
series.fills.template.setAll({
fillOpacity: 0.5, // a half-transparent fill...
visible: true // ...which line series hide by default
});
series.data.setAll(data);
// Color the part below zero: a series axis range from far below the data up to 0
// draws that stretch of the line and fill in its own color
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/axis-ranges/#series-axis-ranges
var rangeDataItem = yAxis.makeDataItem({
value: -1000,
endValue: 0
});
var range = series.createAxisRange(rangeDataItem);
// below zero in the theme's color for negative values (red by default), so it suits every theme and dark mode
var negativeColor = root.interfaceColors.get("negative");
range.strokes.template.setAll({
stroke: negativeColor, // that color below zero...
strokeWidth: 3 // ...as thick as the main line
});
range.fills.template.setAll({
fill: negativeColor, // and a fill in it
fillOpacity: 0.5,
visible: true // line fills are hidden by default
});
// Add cursor
chart.set(
"cursor",
am5xy.XYCursor.new(root, {
// drag across the plot to zoom into those days
behavior: "zoomX",
xAxis: xAxis // the cursor snaps to whole days
})
);
xAxis.set( // the cursor shows the date on this axis...
"tooltip",
am5.Tooltip.new(root, {})
);
yAxis.set( // ...and the value on this one
"tooltip",
am5.Tooltip.new(root, {})
);
// Add scrollbar, hidden at first
// https://www.amcharts.com/docs/v5/charts/xy-chart/scrollbars/
chart.set("scrollbarX", am5.Scrollbar.new(root, { orientation: "horizontal", forceHidden: true }));
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
series.appear(1000);
chart.appear(1000, 100);
{
"type": "XYChart",
"settings": {
"wheelY": "zoomX",
"cursor": {
"type": "XYCursor",
"settings": {
"behavior": "zoomX",
"xAxis": "#axis-0"
}
},
"scrollbarX": {
"type": "Scrollbar",
"settings": {
"orientation": "horizontal",
"forceHidden": true
}
}
},
"properties": {
"series": [
"#series-0"
],
"xAxes": [
"#axis-0"
],
"yAxes": [
"#axis-1"
]
},
"refs": [
{
"data-0": [
{
"date": 1778965200343,
"value": 29
},
{
"date": 1779051600343,
"value": 31
},
{
"date": 1779138000343,
"value": 35
},
{
"date": 1779224400343,
"value": 40
},
{
"date": 1779310800343,
"value": 43
},
{
"date": 1779397200343,
"value": 47
},
{
"date": 1779483600343,
"value": 44
},
{
"date": 1779570000343,
"value": 49
},
{
"date": 1779656400343,
"value": 52
},
{
"date": 1779742800343,
"value": 50
},
{
"date": 1779829200343,
"value": 50
},
{
"date": 1779915600343,
"value": 54
},
{
"date": 1780002000343,
"value": 57
},
{
"date": 1780088400343,
"value": 59
},
{
"date": 1780174800343,
"value": 58
},
{
"date": 1780261200343,
"value": 59
},
{
"date": 1780347600343,
"value": 58
},
{
"date": 1780434000343,
"value": 62
},
{
"date": 1780520400343,
"value": 64
},
{
"date": 1780606800343,
"value": 66
},
{
"date": 1780693200343,
"value": 64
},
{
"date": 1780779600343,
"value": 66
},
{
"date": 1780866000343,
"value": 68
},
{
"date": 1780952400343,
"value": 70
},
{
"date": 1781038800343,
"value": 71
},
{
"date": 1781125200343,
"value": 71
},
{
"date": 1781211600343,
"value": 72
},
{
"date": 1781298000343,
"value": 71
},
{
"date": 1781384400343,
"value": 66
},
{
"date": 1781470800343,
"value": 67
},
{
"date": 1781557200343,
"value": 67
},
{
"date": 1781643600343,
"value": 66
},
{
"date": 1781730000343,
"value": 61
},
{
"date": 1781816400343,
"value": 56
},
{
"date": 1781902800343,
"value": 52
},
{
"date": 1781989200343,
"value": 49
},
{
"date": 1782075600343,
"value": 48
},
{
"date": 1782162000343,
"value": 45
},
{
"date": 1782248400343,
"value": 41
},
{
"date": 1782334800343,
"value": 40
},
{
"date": 1782421200343,
"value": 41
},
{
"date": 1782507600343,
"value": 38
},
{
"date": 1782594000343,
"value": 34
},
{
"date": 1782680400343,
"value": 36
},
{
"date": 1782766800343,
"value": 36
},
{
"date": 1782853200343,
"value": 32
},
{
"date": 1782939600343,
"value": 31
},
{
"date": 1783026000343,
"value": 29
},
{
"date": 1783112400343,
"value": 27
},
{
"date": 1783198800343,
"value": 25
},
{
"date": 1783285200343,
"value": 21
},
{
"date": 1783371600343,
"value": 15
},
{
"date": 1783458000343,
"value": 16
},
{
"date": 1783544400343,
"value": 15
},
{
"date": 1783630800343,
"value": 13
},
{
"date": 1783717200343,
"value": 9
},
{
"date": 1783803600343,
"value": 9
},
{
"date": 1783890000343,
"value": 4
},
{
"date": 1783976400343,
"value": 2
},
{
"date": 1784062800343,
"value": -1
},
{
"date": 1784149200343,
"value": -2
},
{
"date": 1784235600343,
"value": -6
},
{
"date": 1784322000343,
"value": -5
},
{
"date": 1784408400343,
"value": -11
},
{
"date": 1784494800343,
"value": -10
},
{
"date": 1784581200343,
"value": -10
},
{
"date": 1784667600343,
"value": -12
},
{
"date": 1784754000343,
"value": -14
},
{
"date": 1784840400343,
"value": -14
},
{
"date": 1784926800343,
"value": -14
},
{
"date": 1785013200343,
"value": -15
},
{
"date": 1785099600343,
"value": -19
},
{
"date": 1785186000343,
"value": -20
},
{
"date": 1785272400343,
"value": -25
},
{
"date": 1785358800343,
"value": -29
},
{
"date": 1785445200343,
"value": -32
},
{
"date": 1785531600343,
"value": -30
},
{
"date": 1785618000343,
"value": -31
},
{
"date": 1785704400343,
"value": -33
},
{
"date": 1785790800343,
"value": -34
},
{
"date": 1785877200343,
"value": -35
},
{
"date": 1785963600343,
"value": -36
},
{
"date": 1786050000343,
"value": -34
},
{
"date": 1786136400343,
"value": -38
},
{
"date": 1786222800343,
"value": -36
},
{
"date": 1786309200343,
"value": -37
},
{
"date": 1786395600343,
"value": -39
},
{
"date": 1786482000343,
"value": -39
},
{
"date": 1786568400343,
"value": -41
},
{
"date": 1786654800343,
"value": -43
},
{
"date": 1786741200343,
"value": -43
},
{
"date": 1786827600343,
"value": -40
},
{
"date": 1786914000343,
"value": -39
},
{
"date": 1787000400343,
"value": -36
},
{
"date": 1787086800343,
"value": -32
},
{
"date": 1787173200343,
"value": -33
},
{
"date": 1787259600343,
"value": -32
},
{
"date": 1787346000343,
"value": -34
},
{
"date": 1787432400343,
"value": -31
},
{
"date": 1787518800343,
"value": -26
},
{
"date": 1787605200343,
"value": -28
},
{
"date": 1787691600343,
"value": -26
},
{
"date": 1787778000343,
"value": -28
},
{
"date": 1787864400343,
"value": -29
},
{
"date": 1787950800343,
"value": -26
},
{
"date": 1788037200343,
"value": -22
},
{
"date": 1788123600343,
"value": -17
},
{
"date": 1788210000343,
"value": -14
},
{
"date": 1788296400343,
"value": -10
},
{
"date": 1788382800343,
"value": -11
},
{
"date": 1788469200343,
"value": -10
},
{
"date": 1788555600343,
"value": -8
},
{
"date": 1788642000343,
"value": -5
},
{
"date": 1788728400343,
"value": 1
},
{
"date": 1788814800343,
"value": 1
},
{
"date": 1788901200343,
"value": 5
},
{
"date": 1788987600343,
"value": 10
},
{
"date": 1789074000343,
"value": 13
},
{
"date": 1789160400343,
"value": 14
},
{
"date": 1789246800343,
"value": 19
},
{
"date": 1789333200343,
"value": 19
},
{
"date": 1789419600343,
"value": 22
},
{
"date": 1789506000343,
"value": 26
},
{
"date": 1789592400343,
"value": 27
},
{
"date": 1789678800343,
"value": 29
},
{
"date": 1789765200343,
"value": 33
},
{
"date": 1789851600343,
"value": 35
},
{
"date": 1789938000343,
"value": 35
},
{
"date": 1790024400343,
"value": 35
},
{
"date": 1790110800343,
"value": 35
},
{
"date": 1790197200343,
"value": 33
},
{
"date": 1790283600343,
"value": 32
},
{
"date": 1790370000343,
"value": 32
},
{
"date": 1790456400343,
"value": 31
},
{
"date": 1790542800343,
"value": 36
},
{
"date": 1790629200343,
"value": 35
},
{
"date": 1790715600343,
"value": 34
},
{
"date": 1790802000343,
"value": 39
},
{
"date": 1790888400343,
"value": 44
},
{
"date": 1790974800343,
"value": 44
},
{
"date": 1791061200343,
"value": 42
},
{
"date": 1791147600343,
"value": 40
},
{
"date": 1791234000343,
"value": 43
},
{
"date": 1791320400343,
"value": 48
},
{
"date": 1791406800343,
"value": 52
},
{
"date": 1791493200343,
"value": 56
},
{
"date": 1791579600343,
"value": 53
},
{
"date": 1791666000343,
"value": 53
},
{
"date": 1791752400343,
"value": 52
},
{
"date": 1791838800343,
"value": 49
}
]
},
{
"axis-0": {
"type": "DateAxis",
"settings": {
"baseInterval": {
"timeUnit": "day",
"count": 1
},
"renderer": {
"type": "AxisRendererX",
"settings": {
"minorGridEnabled": true,
"cellStartLocation": 0.2,
"cellEndLocation": 0.8
}
},
"tooltip": {
"type": "Tooltip"
}
}
}
},
{
"axis-1": {
"type": "ValueAxis",
"settings": {
"extraTooltipPrecision": 1,
"renderer": {
"type": "AxisRendererY",
"settings": {
"minGridDistance": 30
}
},
"tooltip": {
"type": "Tooltip"
}
}
}
},
{
"series-0": {
"type": "SmoothedXLineSeries",
"settings": {
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "value",
"valueXField": "date",
"tooltip": {
"type": "Tooltip",
"settings": {
"labelText": "{valueX.formatDate()}: {valueY}",
"pointerOrientation": "horizontal"
}
}
},
"properties": {
"data": "#data-0",
"strokes": {
"properties": {
"template": {
"settings": {
"strokeWidth": 3
}
}
}
},
"fills": {
"properties": {
"template": {
"settings": {
"fillOpacity": 0.5,
"visible": true
}
}
}
}
}
}
},
{
"seriesAxisRange-0": {
"axis": "#axis-1",
"series": "#series-0",
"settings": {
"value": -1000,
"endValue": 0
},
"__parseLogic": "axisRange",
"fills": {
"fill": "@root.interfaceColors.get('negative')",
"fillOpacity": 0.5,
"visible": true
},
"strokes": {
"stroke": "@root.interfaceColors.get('negative')",
"strokeWidth": 3
}
}
}
]
}