| name | pbir-conditional-formatting |
| description | Use when adding conditional formatting to a Power BI visual — per-point bar/column colors, diverging gradients, line segment colors, marker transparency, data bars, conditional icons. Three approaches — measure-based (extension measure dataType Text returning theme tokens good/bad/neutral/minColor/midColor/maxColor), FillRule linearGradient2/linearGradient3, rule-based Conditional.Cases. Plus dataViewWildcard selector with matchingOption 1 for per-point, required two-entry array pattern for dataPoint/lineStyles/error, extension measures attaching to EXISTING semantic-model entities via reportExtensions.json, Schema extension on report-level measures, line segment limits (single-series only, segmentGradient affects strokeColor only), marker color/shape/size NOT CF-able (use transparency), ComparisonKind codes 0-4, ScopedEval + AllRolesRef for global min/max. Invoke when user colors bars by a measure, fixes same-color-on-all-points, adds a gradient, data bars, or conditional icons. |
| paths | ["**/visuals/**/visual.json","**/definition/reportExtensions.json"] |
PBIR Conditional Formatting Reference
Three approaches: measure-based (flexible), FillRule gradients (no DAX), and rule-based Conditional (UI-generated).
Approach Matrix
| Approach | Best For | Pros | Cons |
|---|
| Measure-based | Custom logic, theme colors | Full DAX, readable | Requires extension measure |
linearGradient2 / linearGradient3 | Color scales | No DAX | Gradients only |
Conditional.Cases | UI-portable rules | Power BI UI round-trips | Verbose |
Supported Properties
Not every property accepts measure expressions. These do:
fill, borderColor, defaultColor, fontColor, color, backgroundColor, lineColor, markerColor, strokeColor, text, titleText, fontSize, strokeWidth, weight, transparency, radius, url, good, bad, neutral, target, icon
Everything else is literal-only or ThemeDataColor.
dataViewWildcard Selector
The key to per-point formatting.
matchingOption | Behavior |
|---|
0 | Identities + totals (series-level) |
1 | Per data point (most common for CF) |
2 | Totals only |
"selector": {"data": [{"dataViewWildcard": {"matchingOption": 1}}]}
Wrong for per-point: "selector": {"metadata": "Sales.Revenue"} — evaluates once per series.
Two-Entry Array Pattern (Required)
dataPoint, lineStyles, error require a two-entry array: base entry + conditional entry.
"dataPoint": [
{"properties": {}},
{
"properties": {
"fill": {"solid": {"color": {"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Formatting"}},
"Property": "Bar Color"
}}}}}
},
"selector": {"data": [{"dataViewWildcard": {"matchingOption": 1}}]}
}
]
Extension Measure (for color CF)
Defined in Report.Report/definition/reportExtensions.json. dataType MUST be "Text" for color measures. Must attach to an EXISTING entity from the model.
{
"name": "extension",
"entities": [{
"name": "_Formatting",
"measures": [{
"name": "Bar Color",
"dataType": "Text",
"expression": "SWITCH(TRUE(), [Value]<10, \"bad\", [Value]<50, \"neutral\", \"good\")"
}]
}]
}
Theme color names returned by measures: "bad", "good", "neutral", "minColor", "midColor", "maxColor".
Measure Reference in CF Expressions
{"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Formatting"}},
"Property": "Bar Color"
}}}
"Schema": "extension" is required for report-level extension measures. Omit for model measures.
Pattern — Bar / Column Fill
"dataPoint": [
{"properties": {}},
{
"properties": {
"fill": {"solid": {"color": {"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Formatting"}},
"Property": "Bar Color"
}}}}}
},
"selector": {"data": [{"dataViewWildcard": {"matchingOption": 1}}]}
}
]
Pattern — linearGradient2 (Two-Color)
Data-driven (scale to observed range):
{"FillRule": {"linearGradient2": {
"min": {"color": {"Literal": {"Value": "'minColor'"}}},
"max": {"color": {"Literal": {"Value": "'maxColor'"}}},
"nullColoringStrategy": {"strategy": {"Literal": {"Value": "'asZero'"}}}
}}}
Explicit bounds (fixed thresholds):
{"FillRule": {"linearGradient2": {
"min": {"color": {"Literal": {"Value": "'minColor'"}}, "value": {"Literal": {"Value": "0D"}}},
"max": {"color": {"Literal": {"Value": "'maxColor'"}}, "value": {"Literal": {"Value": "1D"}}},
"nullColoringStrategy": {"strategy": {"Literal": {"Value": "'asZero'"}}, "color": {"Literal": {"Value": "'#FFFFFF'"}}}
}}}
Pattern — linearGradient3 (Diverging)
{"FillRule": {"linearGradient3": {
"min": {"color": {"Literal": {"Value": "'minColor'"}}, "value": {"Literal": {"Value": "-1D"}}},
"mid": {"color": {"Literal": {"Value": "'neutral'"}}, "value": {"Literal": {"Value": "0D"}}},
"max": {"color": {"Literal": {"Value": "'maxColor'"}}, "value": {"Literal": {"Value": "1D"}}},
"nullColoringStrategy": {"strategy": {"Literal": {"Value": "'asZero'"}}}
}}}
Pattern — Line Segment Colors (Single-Series Only)
Multi-series line charts cannot use segment coloring. Measure must return hex.
"lineStyles": [
{"properties": {"segmentGradient": {"expr": {"Literal": {"Value": "true"}}}}},
{
"properties": {
"strokeColor": {"solid": {"color": {"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Formatting"}},
"Property": "Line Color"
}}}}}
},
"selector": {"data": [{"dataViewWildcard": {"matchingOption": 1}}]}
}
]
segmentGradient only affects strokeColor. It does NOT work with lineStyle, strokeWidth, or markerFill.
Pattern — Axis Label Colors
No selector needed.
"categoryAxis": [{
"properties": {
"labelColor": {"solid": {"color": {"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Formatting"}},
"Property": "Axis Color"
}}}}}
}
}]
Pattern — Marker Transparency (0-100)
Marker color / shape / size do NOT support CF. Transparency does.
"markers": [
{"properties": {"borderShow": {"expr": {"Literal": {"Value": "false"}}}}},
{
"properties": {
"transparency": {"expr": {"Measure": {
"Expression": {"SourceRef": {"Schema": "extension", "Entity": "_Measures"}},
"Property": "Marker Opacity"
}}}
},
"selector": {"data": [{"dataViewWildcard": {"matchingOption": 1}}]}
}
]
Pattern — Rule-Based Conditional
{"expr": {"Conditional": {"Cases": [
{"Condition": {...}, "Value": {"Literal": {"Value": "'#FF0000'"}}},
{"Condition": {...}, "Value": {"Literal": {"Value": "'#00FF00'"}}}
]}}}
Cases evaluated in order; first match wins.
ComparisonKind Codes
| Value | Operator |
|---|
0 | Equal |
1 | Greater than |
2 | Greater than or equal |
3 | Less than or equal |
4 | Less than |
Logical Wrappers
{"And": {"Left": {...}, "Right": {...}}}
{"Or": {"Left": {...}, "Right": {...}}}
{"Not": {"Expression": {...}}}
ScopedEval + AllRolesRef (Global Context)
Removes filter context for global min/max:
{"ScopedEval": {"Expression": {"Measure": {...}}, "Scope": [{"AllRolesRef": {}}]}}
Data Bars (table/matrix columns)
"dataBars": {
"positiveColor": {"solid": {"color": {"expr": {"Literal": {"Value": "'#E1EBF2'"}}}}},
"negativeColor": {"solid": {"color": {"expr": {"Literal": {"Value": "'#E5B97D'"}}}}},
"axisColor": {"solid": {"color": {"expr": {"Literal": {"Value": "'#FFFFFF'"}}}}},
"reverseDirection": {"expr": {"Literal": {"Value": "false"}}},
"hideText": {"expr": {"Literal": {"Value": "false"}}}
}
Conditional Icons
"icon": {
"kind": "Icon",
"layout": {"expr": {"Literal": {"Value": "'IconOnly'"}}},
"value": {"expr": {"Conditional": {"Cases": [
{"Condition": {"Comparison": {"ComparisonKind": 4, "Left": {"Measure": {...}}, "Right": {"Literal": {"Value": "0D"}}}},
"Value": {"Literal": {"Value": "'SymbolMedium'"}}}
]}}}
}
Gotchas
| Issue | Cause | Fix |
|---|
| All points get same color | metadata selector used | Switch to dataViewWildcard with matchingOption: 1 |
| CF works intermittently | Single-entry array with selector | Use two-entry array pattern |
| Measure rejected as color | dataType not "Text" in reportExtensions.json | Set "dataType": "Text" |
| Extension measure not found | Created new entity in reportExtensions.json | Entities must already exist in the semantic model |
| Line segment colors don't work | Chart has multiple series in Y/Y2 | Segment coloring requires a single series |
| Marker color CF ignored | Marker fill/shape/size don't support CF | Use transparency instead |
segmentGradient + strokeWidth silently fails | Only strokeColor is supported | Don't combine |
reportExtensions.json deserialization fails | Empty entities: [] | Delete the file entirely when no extension measures exist |
title / legend selector ignored | These objects don't accept selectors | Apply globally (no selector) |
| Gradient midpoint shifts unexpectedly | Data-driven bounds when fixed needed | Switch to explicit bounds form with value on min/mid/max |
AllRolesRef scope wrong place | Nested incorrectly | ScopedEval.Scope: [{"AllRolesRef": {}}] at the outer wrapper |
Reference
See also
pbir-visual-json — selectors, expressions, literal suffixes
pbir-themes — ThemeDataColor and theme token resolution