To solve this week's challenge you'll need the following tools:
- boxy-svg.com/app (create free account to download the svg)
- Text editor tool such as Notepad
- Power BI Desktop
The SVG in the challenge is made up of two circles which is what allows it to be dynamic (think of it as layers on top of each other). The first circle is static in the background and the second circle acts as the dynamic progress ring with a text at its center.
First let's create the canvas in boxy-svg. Set the dimensions of the canvas in Geometry on the right panel to Width: 100 and Height: 100, leaving X and Y at 0 just like below.


Next, on the left hand side select the shape "Ellipse" and create a circle, moving it so it fits inside the small canvas we've created. After clicking on the circle, set its dimensions in Geometry to the below configuration:


In the Fill panel adjust the color so it's transparent:


In the Stroke panel type into the color box the following hex code #EAEAEA (light grey). Set the stroke width to 10.


Save the SVG file to your local machine through File > Save. You will need to create a free account before downloading.
Once you have downloaded the SVG, you can edit it using any text editor - here I've used Notepad. Essentially this is the raw code that you want to use inside your DAX measure for the progress rings.

To make this work in DAX you first need to replace the double quote marks (") with single quotes (') through Edit > Replace.


In Power BI Desktop, create a new measure to preview your SVG visual. Name it SVG and paste your SVG code inside double quotes like below:
SVG =
"
<?xml version='1.0' encoding='utf-8'?>
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
<ellipse style='stroke-width: 6px; paint-order: fill; fill: rgba(255, 255, 255, 0); stroke: rgb(234, 234, 234);' cx='50' cy='50' rx='40' ry='40'/>
</svg>
"
Inside the same DAX measure you want to replace the <?xml…> line (first line) with the following:
"data:image/svg+xml;utf8,"
This tells Power BI that the image data is formatted in UTF-8 encoded XML/SVG.
So, the full code should be:
SVG =
"
data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
<ellipse style='stroke-width: 6px; paint-order: fill; fill: rgba(255, 255, 255, 0); stroke: rgb(234, 234, 234);' cx='50' cy='50' rx='40' ry='40'/>
</svg>
"
Remember to change the data category to Image URL, otherwise your icon won't appear as a visual.

Because that's only giving us the background circle but we want a progress ring we need to copy and paste the initial circle and lay it on top of the original circle in boxy-svg. Make sure their co-ordinates are matching using the same Geometry configuration plus an additional setting of -90 degree rotation (to shift the start point of the circle from 3 o'clock to 12 o'clock).

Then leave the Fill transparent like before and change the Stroke color to a different one so you can distinguish it (I picked green #06D6A0) while keeping the same Stroke width. In the Stroke panel scroll down and change the Cap setting to round. This will give that clean look of where the ring breaks.

Finally to add the percentage value placeholder in the middle of the ring you'll need to select the Text tool and click near the centre of the canvas typing 75% (placeholder). Don't worry about the target value, we will be manipulating the DAX code to make this value dynamic later on. In the Typography panel set the font to Arial size 18, anchor to middle. I had to manually move the text so it lines up at the centre and I've included the Geometry configurations below to help you.



Replace the previous SVG code with the new one which should look like the one below (remember to replace " with '). Now that you have your custom visual it's time to build the numeric parameter and your DAX measures.

First of all, you'll need to create a New Parameter (Modeling > New Parameter). Select Numeric range from the drop-down and give it an appropriate name like Target Goal ($). Set the data type to Whole number with a Min value of $20M and a Max value of $30M, you can decide on the increment and default values. Add the slicer to the page.
In the Format visual panel inside Visual > Values I set the target value size to 12. Then, under General > Data Format I selected the format to be currency, specifically in $ with 0 decimal places. I've added some effects to the slicer by turning on Background, Visual border and Shadow in General > Effects. I also added 5px padding to the top part.
Next, you'll need to create a DAX measure to calculate the Total Sales:
Total Sales =
SUM(financials[Sales])
Now you'll need a second DAX measure named SVG Goal Ring to combine the target goal value, the total sales and the SVG using variables:
SVG Goal Ring =
VAR Target = 'Target Goal ($)'[Target Goal ($) Value]
VAR Actual = [Total Sales]
-- Capped percentage for drawing the circle stroke (stops at 100%)
VAR Pct = MIN(DIVIDE(Actual,Target,0),1.0)
-- Percentage for the text label
VAR PctText = FORMAT(DIVIDE(Actual,Target,0),"0.0%")
-- Calculate the circumference of the circle to determine stroke length, then offset the stroke length by the progress percentage from above
VAR Radius = 40
VAR Circumference = 2 * 3.14159 * Radius
VAR DashArray = Circumference
VAR DashOffset = Circumference * (1-Pct)
VAR Ringcolor =
SWITCH(
TRUE(),
Pct >= 1.0, "#06D6A0", – Green
Pct >= 0.75, "#FFD166", – Yellow
"#FF6B6B" – Red
)
VAR SVG =
"data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
<ellipse style='paint-order: fill; fill: rgba(255, 255, 255, 0); stroke: rgb(234, 234, 234); stroke-width: 10px;' cx='50' cy='50' rx='40' ry='40'/>
<ellipse style='fill: rgba(216, 216, 216, 0); stroke-width: 10px; stroke: " & Ringcolor & "; stroke-dasharray: " & Circumference & "; stroke-dashoffset: " & DashOffset & "; stroke-linecap: round; transform-box: fill-box; transform-origin: 50% 50%;' cx='50' cy='50' rx='40' ry='40' transform='matrix(0, -1, 1, 0, 0, -0.000006)'/>
<text style='fill: rgb(51, 51, 51); font-family: Arial, sans-serif; font-size: 18px; text-anchor: middle; white-space: pre;' x='-15.506' y='-3.721'><tspan x='52.51' y='67.405' style='baseline-shift: super; font-size: 18px; word-spacing: 0px;'>" & PctText & "</tspan></text>
</svg>"
RETURN SVG
Note how the underlying SVG code remains the same while we replaced and added a few things in for the top circle SVG and the text placeholder to make it dynamic based on the Target Value chosen by the user.
Finally, to add the colored bards in the Total Sales column turn Data bars on in the format visual panel, Visual > Cell elements > Select Total Sales > Switch on Data Bars.
Now you know how to create custom SVG visuals without the need for AI! I hope you enjoyed re-creating this Workout Wednesday Challenge as much as I have 😊
