Let’s forego the usual circles and bars we typically see used in charts for more eccentric shapes. With online presentations more and more common today, a quick way to spruce up your web slides and make them stand out is to give the charts a shapely makeover 🪄
I’ll show you how to create charts with interesting shapes using glyphs, CSS shapes, and emojis with minimal effort.
Let’s start with a simple example.
Using glyphs
<div id="chart">
<div id="chart-shape">⬠</div>
<div id="chart-value"></div>
</div>
#chart {
width: 300px;
height: 300px;
display: grid;
background: white;
}
#chart * {
height: inherit;
grid-area: 1 / 1;
}
We first give the chart some dimensions and stack the two div inside it by assigning them to the same grid cell. They can be stacked up using any other way, too — with position
property, for instance.
Look at the HTML above one more time. One of the divs has a pentagon symbol — the chart shape we want. I added that symbol using the “Emoji and Symbols” keyboard, though it can also be done with the HTML entity value for pentagon, ⬠
, inside the div.
The div with the symbol is then styled with CSS font properties as well as a desired chart color. It’s large enough and centered.
#chart-shape {
font: 300px/300px serif;
text-align: center;
color: limegreen;
}
To the second div in the HTML contains a conic gradient background image. The percentage of the gradient represents the visual value of the chart. The same div also has mix-blend-mode: screen;
.
#chart-value {
background: conic-gradient(transparent 75%, darkseagreen 75%);
mix-blend-mode: screen;
}
The mix-blend-mode
property blends colors inside an element with its backdrop. The screen
blend mode value causes a lighter blend to come through. A lighter green shows through the portion where the darkseagreen
colored part of the conic gradient overlaps with the limegreen
colored pentagram, while the rest of the darskseagreen
gradient disappears against the white backdrop of the chart.
An alternative to adding the chart shape in the HTML is to add it as another background layer in CSS and use background-blend-mode
instead of mix-blend-mode
. However, the code for a chart shape inside the CSS can be less legible for a quick glance. So it’s up to you to see where it’ll be easier for you to add the chart shape in: HTML or CSS. You’ve both options.
#chart {
width: 300px;
height: 300px;
background:
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><div xmlns='http://www.w3.org/1999/xhtml' style='font:300px/300px serif;color:limegreen;text-align: center;background:white'>⬠</div></foreignObject></svg>"),
conic-gradient(transparent 75%, darkseagreen 75%);
background-blend-mode: screen;
}
The pentagon symbol is added as a background image in addition to the conic gradient. Then, finally, the property-value pair background-blend-mode: screen;
kicks in and the result looks same as the previous demo.
The pentagon background image is created by embedding the HTML with the pentagon symbol (⬠
) into an SVG that is embedded into a data URL.
<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
<foreignObject width='300px' height='100%'>
<div xmlns='http://www.w3.org/1999/xhtml'
style='
font:300px/300px serif;
color:limegreen;
text-align: center;
background:white;'>
⬠
</div>
</foreignObject>
</svg>
Which becomes this in CSS:
background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><div xmlns='http://www.w3.org/1999/xhtml' style='font:300px/300px serif;color:limegreen;text-align: center;background:white'>⬠</div></foreignObject></svg>");
Using CSS shapes
Next, let’s use CSS shapes in place of symbols. CSS shapes are primarily created with the use of border properties. We have a collection of CSS shapes in our archive for your reference.
Here’s a set of properties that can create a simple triangle shape in an element we’ll later add to the SVG, replacing the symbol:
border: 150px solid white;
border-bottom: 300px solid lime;
border-top: unset;
When combined with the conic gradient and the background blend, the result is:
<div id="chart"></div>
#chart {
width: 300px;
height: 300px;
background:
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><html xmlns='http://www.w3.org/1999/xhtml'><div style='border:150px solid white; border-bottom:300px solid lime; border-top:unset'></div><div style='border:150px solid transparent; border-bottom:300px solid white; border-top:unset; transform:scale(0.8) translateY(-360px);'></div></html></foreignObject></svg>"),
conic-gradient(transparent 75%, darkseagreen 75%);
background-blend-mode: screen;
}
To restrict the design to the border, a smaller white triangle was added to the design.
<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
<foreignObject width='300px' height='100%'>
<html xmlns='http://www.w3.org/1999/xhtml'>
/* green triangle */
<div style='
border: 150px solid white;
border-bottom: 300px solid lime;
border-top: unset'></div>
/* smaller white triangle */
<div style='
border: 150px solid transparent;
border-bottom: 300px solid white;
border-top: unset;
transform: scale(0.8) translateY(-360px);'></div>
</html>
</foreignObject>
</svg>
Which, again, becomes this in CSS:
background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><html xmlns='http://www.w3.org/1999/xhtml'><div style='border:150px solid white; border-bottom:300px solid lime; border-top:unset'></div><div style='border:150px solid transparent; border-bottom:300px solid white; border-top:unset; transform:scale(0.8) translateY(-360px);'></div></html></foreignObject></svg>");
Using emojis
Will emojis work with this approach to charts? You bet it will! 🥳
A block-colored emoji is fed into the SVG image the same way the HTML symbols are. The block color of the emoji is created by giving it a transparent color
value, followed by adding a desired color as text-shadow
. I covered this technique in another post.
<div id="chart"></div>
#chart {
width: 300px;
height: 300px;
background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='300px'><body style='margin:0;text-align:center;color:transparent;' xmlns='http://www.w3.org/1999/xhtml'><div style='text-shadow: 0 0 limegreen;font:200px/300px serif;background:white;'>🍏</div><div style='text-shadow:0 0 white;font:170px/300px serif;position:relative;top:-300px;'>🍏</div></body></foreignObject></svg>"),
conic-gradient(transparent 64%, darkseagreen 64%);
background-blend-mode: screen;
}
Just as with the last demo, a smaller white apple shape is added at the center to create the border design.
<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
<foreignObject width='300px' height='300px'>
<body xmlns='http://www.w3.org/1999/xhtml' style='
margin: 0;
text-align: center;
color:transparent;'>
/* green apple shape */
<div style='
text-shadow: 0 0 limegreen;
font-size: 200px;
background: white;'>🍏</div>
/* smaller white apple shape */
<div style='
text-shadow:0 0 white;
font-size: 170px;
position: relative;
top: -300px;'>🍏</div>
</body>
</foreignObject>
</svg>
I added the two divs inside the <body>
element so the repeating style properties of the divs are declared only once in the body element. The divs will then automatically inherit those properties.
Chris had the idea to animate the conic gradient — its percent value to be specific — using the CSS @property
(supported in Chrome at the time of writing this article), and it just has the most beautiful effect on the design. @property
is a CSS at-rule that explicitly defines a custom CSS property. In supported browsers, when a custom property is defined using @property
it can be animated.
@property --n {
syntax: '<percentage>';
inherits: true;
initial-value: 30%;
}
#chart {
width: 300px;
height: 300px;
--n: 30%; /*declaration for browsers with no @property support */
background:
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='300px'><body style='margin:0;text-align:center;color:transparent;' xmlns='http://www.w3.org/1999/xhtml'><div style='text-shadow: 0 0 limegreen;font:200px/300px serif;background:white;'>🍏</div><div style='text-shadow:0 0 white;font:170px/300px serif;position:relative;top:-300px;'>🍏</div></body></foreignObject></svg>"),
conic-gradient(transparent var(--n), darkseagreen var(--n));
background-blend-mode: screen;
transition: --n 2s ease-in-out
}
#chart:hover { --n: 70%; }
The chart above will change its value on hover. In Chrome, the change will look animated.
And although it won’t be as smooth as CSS animation, you can also try animating the gradient using JavaScript. The following JavaScript will cause a somewhat similar animating effect as the above when the cursor moves over the chart.
const chart = document.querySelector('#chart')
chart.onpointerover = ()=>{
var i = 30,
timer = setInterval(()=> {
if (i < 70)
chart.style.setProperty('--n', i++ + '%')
else clearInterval(timer)
}, 10)
}
chart.onpointerout = ()=>{
var i = 70,
timer = setInterval(()=> {
if (i >= 30)
chart.style.setProperty('--n', i-- + '%')
else clearInterval(timer)
}, 10)
}
When trying your own designs, keep in mind how the different blend modes work. I used the screen
blend mode in all my demos just to keep things simple. But with different blend modes, and different backdrop colors, you’ll get varying results. So, I recommend going deeper into blend modes if you haven’t already.
Also, if you want to exclude an element’s color from the final result, try isolation: isolate;
on the element — the browser will ignore that backdrop color when applying the blend.
And even though there are all kinds of unusual and quirky shapes we can use in any wild colors we want, always be mindful of the legibility by making the chart value large and clear enough to read.
Great article! This should be more famous and have more comments. Note: But the hexagon charcter is not supported in chrome android.