DAX SVG measure · Comparison
IBCS variance bars and pins in Power BI with DAX
By Nawaf Sharaf · Founder & Reliability Engineer · Updated
Use it when
- Finance tables comparing actual (AC) with plan (PL) or prior year (PY).
- You follow or are moving towards IBCS-style reporting.
- Some lines are costs, where under plan is favourable.
Skip it when
The table has very different magnitudes per row (revenue next to a small cost line): the absolute bars of small lines vanish. Use the relative pin.
Three looks
The variance bar (ibcs style) in three palettes; click one to edit it.
Data it needs
The builder asks for exactly these fields; type your own measure and column names there.
- Row column
- The column on the table's rows (account, region).
- Actual (AC)
- Actual measure.
- Plan or prior year
- Reference measure. ΔPL% divides by |PL|, so a zero plan returns BLANK().
The full DAX
Swap 'Region'[Region] and [Sales] and [Sales Plan] for your own names.
Variance Bar =
-- VizForge SVG measure: absolute variance bar (ΔPL), scaled across every row in the visual.
-- Set Measure tools > Data category = Image URL. The row column must be the column on the table's rows.
-- IBCS notation: green = favourable, red = unfavourable; the axis is the plan.
-- Made with the free VizForge SVG measure builder: vizforge.ai/tools/svg-measure-builder
-- Need per-point tooltips, click-to-select inside the chart or Format-pane settings? Generate it as a real custom visual: vizforge.ai/visuals
VAR _Var = [Sales] - [Sales Plan]
VAR _Rows = ADDCOLUMNS ( ALLSELECTED ( 'Region'[Region] ), "@a", [Sales], "@p", [Sales Plan] )
VAR _Max = MAXX ( _Rows, ABS ( [@a] - [@p] ) )
VAR _Colour = IF ( _Var >= 0, "%231AAB40", "%23D64554" )
RETURN
IF (
ISBLANK ( _Var ),
BLANK (),
"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='18' viewBox='0 0 160 18'><rect x='"
& FORMAT ( MIN ( 57, 57 + DIVIDE ( _Var, _Max, 0 ) * 54 ), "0.0", "en-US" )
& "' y='3.5' width='"
& FORMAT ( ABS ( 57 + DIVIDE ( _Var, _Max, 0 ) * 54 - 57 ), "0.0", "en-US" )
& "' height='11' fill='"
& _Colour
& "'/><rect x='56.5' y='0' width='1' height='18' fill='%23252423'/><text x='160' y='9' dy='0.35em' text-anchor='end' font-family='Segoe UI, Helvetica, Arial, sans-serif' font-size='11' fill='%23252423'>"
& FORMAT ( _Var, "+#,0.0,""K"";-#,0.0,""K"";0", "en-US" )
& "</text></svg>"
)DAX user-defined function variant (VizForge.SvgVarianceBar)
One-line call: Variance Bar = VizForge.SvgVarianceBar ( 'Region'[Region], [Sales], [Sales Plan] )
-- 1) Paste into DAX query view and choose 'Update model with changes'.
-- Needs DAX user-defined functions (generally available since the June 2026 Power BI release).
DEFINE
/// Variance bar (IBCS style) as an SVG image URL (VizForge). Optional parameters default to the colours you picked.
FUNCTION VizForge.SvgVarianceBar = (
pCategory : ANYREF EXPR,
pActual : ANYREF EXPR,
pPlan : ANYREF EXPR,
pGoodColor : STRING = "#1AAB40",
pBadColor : STRING = "#D64554",
pAxisColor : STRING = "#252423",
pLabelColor : STRING = "#252423"
) =>
VAR _Var = pActual - pPlan
VAR _Rows = ADDCOLUMNS ( ALLSELECTED ( pCategory ), "@a", CALCULATE ( pActual ), "@p", CALCULATE ( pPlan ) )
VAR _Max = MAXX ( _Rows, ABS ( [@a] - [@p] ) )
VAR _Colour = IF (
_Var >= 0,
SUBSTITUTE ( pGoodColor, "#", "%23" ),
SUBSTITUTE ( pBadColor, "#", "%23" )
)
RETURN
IF (
ISBLANK ( _Var ),
BLANK (),
"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='18' viewBox='0 0 160 18'><rect x='"
& FORMAT ( MIN ( 57, 57 + DIVIDE ( _Var, _Max, 0 ) * 54 ), "0.0", "en-US" )
& "' y='3.5' width='"
& FORMAT ( ABS ( 57 + DIVIDE ( _Var, _Max, 0 ) * 54 - 57 ), "0.0", "en-US" )
& "' height='11' fill='"
& _Colour
& "'/><rect x='56.5' y='0' width='1' height='18' fill='"
& SUBSTITUTE ( pAxisColor, "#", "%23" )
& "'/><text x='160' y='9' dy='0.35em' text-anchor='end' font-family='Segoe UI, Helvetica, Arial, sans-serif' font-size='11' fill='"
& SUBSTITUTE ( pLabelColor, "#", "%23" )
& "'>"
& FORMAT ( _Var, "+#,0.0,""K"";-#,0.0,""K"";0", "en-US" )
& "</text></svg>"
)
EVALUATE { VizForge.SvgVarianceBar ( 'Region'[Region], [Sales], [Sales Plan] ) }
-- 2) Then create the measure and set Measure tools > Data category = Image URL:
-- Variance Bar = VizForge.SvgVarianceBar ( 'Region'[Region], [Sales], [Sales Plan] )
-- Colours and thresholds are optional parameters: pass a value after the fields to override one.
-- Pass colours as plain "#RRGGBB"; the function encodes the # for the data URI.Placing it in a report
Needs 'Region'[Region] on the rows plus [Sales] and [Sales Plan]; 18 × 160. Follow IBCS: put the absolute variance column next to it so readers get the number and the bar. Remember the Image URL data category. All setup steps.
Gotchas
- 'Below plan is good' flips the colours, not the direction of the bar: costs over plan still extend right, in red.
- Scaling uses ALLSELECTED over the row column; totals get their own (larger) variance and look clipped unless hidden.
- IBCS also asks for the scale to match across charts on a page; give every variance column the same width.
- Length. 390 characters, but every row scans all visible rows for the shared scale.
- Context. Every row looks up the largest absolute variance among ALLSELECTED regions, so bars share one scale around a centre line. The total row's variance is usually larger than any region's, so its bar runs past the shared scale; hide it there with ISINSCOPE.
- Tooltip. Hovering shows the SVG text; a blank report-page tooltip hides it.
When an SVG measure isn't enough
A variance chart visual can bridge from plan to actual, show both values on hover and filter the page by account. See the Waterfall custom visual for what an interactive version can do.
KPI card designs using it
Finished designs built on this pattern, from the KPI card gallery.