The target: a two-series comparison bar chart in the Ignition Perspective ApexCharts module. Series 0 holds the base (target) values and series 1 holds the real values. Each real bar should be green when it is at or below its base bar and red when it is above it, independently of the other bars. The usual symptom is that every color edit repaints all bars of the series at once. Get the per-bar color working first, then restore the legend marker the fix removes.
Check 1: Rule out the quick fixes that recolor the whole series
Look at what you changed and how many bars moved.
| What was tried | What you see | Why it fails |
|---|---|---|
Hex string in colors[1]
|
Every real bar changes together | A string in colors is one color for the whole series. It has no idea what value each bar holds. |
Binding or script that writes one hex to colors[1] from a comparison |
Still all bars together, and the result flips as the data changes | The binding produces one result for one property. There is still only one color slot per series. |
Turning on distributed bar coloring (plotOptions.bar.distributed) |
Bars get different colors, but by position, not value; legend changes to per-category | Distributed mode walks the colors array by data point index. It does not compare values. |
If you are on any of these, stop editing hex values. The fix is a JavaScript function in the color slot, which ApexCharts calls once per bar at render time.
Check 2: Confirm series order and data shape
Open the component's property tree and read series.
- Confirm index 0 is the base series and index 1 is the real series. If they are reversed, swap the indexes in the function in Check 5, or the colors come out inverted.
- Look at one element of
series[n].data.-
Plain numbers (for example
[40, 55, 70]): usew.config.series[n].data[i]as written below. Go to Check 3. -
Objects (for example
{x: 'Line 1', y: 40}): the comparison has to read.y. Otherwise it compares objects and every bar falls into the same branch. Add.yto both operands in Check 5, then go to Check 3.
-
Plain numbers (for example
- Confirm both series have the same number of points in the same category order. The function pairs bars by index. A missing or extra point shifts every comparison after it.
Check 3: Find which colors slot drives the real bars
ApexCharts assigns colors[0] to series 0, colors[1] to series 1, and so on. The base series keeps its static hex in colors[0]. The function replaces the value in colors[1].
This is not a Perspective script and not a binding. Put the function text directly as the value of colors[1] in the component's props, in the same place the hex string was. The module passes it through to the chart's colors option, and the chart evaluates it for every bar in that series.
-
If
colorshas fewer entries than there are series: add the missing entry first. ApexCharts falls back to its default palette for unassigned series. -
If the base bars must also be conditional: the same pattern works in
colors[0]. Most comparison charts keep the base a fixed neutral color.
Check 4: Use the per-bar index, not the series index
This is where most first attempts go wrong. ApexCharts calls a color function with one object. The useful members are:
| Member | What it holds | Changes per bar? |
|---|---|---|
value |
The value of the bar being colored | Yes |
seriesIndex |
Which series the bar belongs to | No. Constant for every bar in the series |
dataPointIndex |
Position of the bar within its series (category index) | Yes |
w |
The chart context; w.config.series is the full series data |
No |
A function that looks up the comparison value with seriesIndex compares every real bar against the same single element. You get all bars one color again, only now the color comes from a function. Use dataPointIndex to pull the matching bar from each series.
Reading both operands from w.config.series is more explicit than using value. You can see which series is which, and the function reads whatever data is live at render time. Bound or tag-driven series data recolors on every update without extra logic.
Check 5: Apply the color function
This version turns a bar red when the base is below the real value, and green otherwise. Equal counts as green:
function ({ value, dataPointIndex, w }) {
if (w.config.series[0].data[dataPointIndex] < w.config.series[1].data[dataPointIndex]) {
return '#942222';
} else {
return '#2E6F40';
}
}
- Select the ApexCharts component and expand
propsdown tocolors. - Leave
colors[0]as the base series hex. - Replace the value of
colors[1]with the function text above. - If your data points are objects (Check 2), change both operands to
...data[dataPointIndex].y. - If equal should read as a fault, change
<to<=. - Save and open the view in a session. The designer preview does not always re-render a chart after an options change.
How it runs: for each bar in series 1, ApexCharts calls the function with that bar's dataPointIndex. The function reads the base value and the real value at that same index from w.config.series and returns one hex. Each bar gets its own evaluation, so mixed results are expected: one bar green, the next red.
Check 6: Restore the legend marker the function removed
After the function goes in, the legend marker for the real series usually goes blank. The legend needs one fixed color per series. A function that returns different colors per bar gives it nothing single to draw, so it does not render one.
Set the marker colors explicitly under legend.markers.fillColors. The fix only works if this property is an array with one entry per series, in series order:
legend: {
markers: {
fillColors: ['#XXXXXX', '#2E6F40']
}
}
Replace #XXXXXX with the hex already in colors[0], so the base marker matches the base bars. The second entry is the marker color for the real series. Pick whichever of the two function colors, or a neutral, makes sense for your operators.
| What you entered | Result |
|---|---|
fillColors as a single hex string |
Real-series marker still missing |
fillColors as a string that looks like an array, e.g. "['#...','#...']"
|
Still missing. It is text, not an array. |
fillColors created as an array node with two string elements |
Both markers render |
In the Perspective property editor, add fillColors under legend.markers as an array type. Then add one element per series. Do not type bracket syntax into a string value.
Verify the fix
- Load test data where at least one real value is below its base and one is above. Confirm the bars show different colors in the same render.
- Set one real value exactly equal to its base. Confirm it lands in the branch you chose (green with
<, red with<=). - Change a live value across its base, through the tag or binding. Confirm only that bar changes color and the others hold.
- Check the legend. Both series markers are present, and the base marker matches the base bars.
- Hover a recolored bar. The tooltip still shows the correct series name and value.
- Add or remove a category in the data. All bars remain paired correctly, which proves both series stay index-aligned.
For other options, the ApexCharts options reference (the legend, colors, and plotOptions pages) lists the property paths. The module exposes those same paths under the component's props.
FAQ
Can I color individual bars by value in the Ignition ApexCharts module without a script?
Yes. Put a JavaScript function as the value of the series' entry in colors, such as colors[1]. Have it compare w.config.series[0].data[dataPointIndex] to w.config.series[1].data[dataPointIndex] and return a hex per bar. No Perspective script or binding is needed.
Does seriesIndex work for per-bar color comparisons in ApexCharts?
No. seriesIndex is the same for every bar in a series, so every bar gets compared against one element. Use dataPointIndex, which changes per bar and pairs each real bar with its matching base bar.
Why did my ApexCharts legend marker disappear after using a color function?
The legend cannot derive one marker color from a function that returns different colors per bar. Set legend.markers.fillColors as an array with one hex per series, in series order. A single string, or a string typed to look like an array, does not work.
Stop and escalate if the function text in colors is rendered literally, or ignored entirely, in a runtime session. That points to the installed module version not evaluating function strings, and no amount of chart editing fixes it. Collect the Ignition and module versions, the view's JSON, and the browser console output. Then open a case with the module vendor's support channel or Inductive Automation support.