LWC components with self contained images
When you create your LWC components, it is easy to include Salesforce predefined icons using lightning-icon
. However once you need a custom icon, you point to an external URL, breaking the self containment. Unless you use url(data:)
. Here is what I did
A scalable check mark
For a list selection component I wanted a green check mark, like the picture above, indicate a selected record (more on the component in a later post). Since LWC doesn't allow (yet?) to store image assets inside a bundle and I wanted the component to be self contained.
The solution is to use data:image/svg+xml
for a background image. The details were nicely outlined in css-tricks. I tried to use svg as source code directly, but failed to get it to work. So I resorted to use base64
. It is an additional step, using an online Base64 encoder.
Making images
SVG is just an XML based text format, so you could create your image in notepad (take that jpg!). However you probably want to use a graphic editor. My choice here is Sketch (which gives me funny looks from designers: why a developer uses one of their tools). There were some steps worth to mention:
- When using text (like the check mark), convert that to a svg path. Right click on text and select "Convert to outlines". This allows the text to scale with the rest of the image
- Use the Edit-Copy-Copy SVG Code rather than use the export functionality
- The resulting SVG is "talkative", you can edit and remove quite some content:
- remove width and height attributes from the
<svg>
element, but keep the viewbox. Also remove the xlink name space - the
<g>
element doesn't need any attribute <polygon>
only needsfill
andpoints
attribute- All numeric values have many digits. You can round them up
The resulting svg looks like this:
<svg viewBox="0 0 120 240" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<g>
<polygon fill="#006600" points="0.0 0.0 120.0 0.0 120.0 120.0"></polygon>
<path d="M80.3310547,56.0839844 C79.6279262,54.5410079 79.198243,53.5742207 79.0419922,53.1835938 L77.2548828,49.0527344 C76.7080051,47.7831968 76.2392598,46.7480509 75.8486328,45.9472656 L74.7646484,43.75 C72.6747942,39.6093543 70.2041158,36.6894616 67.3525391,34.9902344 C70.6923995,32.1777203 73.6708854,30.7714844 76.2880859,30.7714844 C78.0263759,30.7714844 79.3935497,31.440423 80.3896484,32.7783203 C81.3857472,34.1162176 82.235348,36.4355304 82.9384766,39.7363281 C88.016627,30.1073737 91.9716655,23.4570496 94.8037109,19.7851562 C97.3427861,16.4843585 99.515616,14.2968804 101.322266,13.2226562 C103.128915,12.1484321 105.526352,11.6113281 108.514648,11.6113281 C110.389658,11.6113281 112.420887,11.9726526 114.608398,12.6953125 C109.510717,16.0351729 103.949249,21.5233993 97.9238281,29.1601562 C91.8984074,36.7969132 86.0342082,45.7714328 80.3310547,56.0839844 Z" id="✓" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>
Important note: The original image was designed 120x120 px which got reflected in the (removed) width and height attributes and the viewbox. I changed the vertical value from 120 to 240, so the svg image would only take half of the horizontal space (see the CSS below for the impact)
Putting it together with CSS
The checkmark should show up once an element has the selected
class assigned. A little CSS does the trick:
.selected {
background-image: url('data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTIwIDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8Zz48cG9seWdvbiBmaWxsPSIjMDA2NjAwIiBwb2ludHM9IjAuMCAwLjAgMTIwLjAgMC4wIDEyMC4wIDEyMC4wIj48L3BvbHlnb24+CjxwYXRoIGQ9Ik04MC4zMzEwNTQ3LDU2LjA4Mzk4NDQgQzc5LjYyNzkyNjIsNTQuNTQxMDA3OSA3OS4xOTgyNDMsNTMuNTc0MjIwNyA3OS4wNDE5OTIyLDUzLjE4MzU5MzggTDc3LjI1NDg4MjgsNDkuMDUyNzM0NCBDNzYuNzA4MDA1MSw0Ny43ODMxOTY4IDc2LjIzOTI1OTgsNDYuNzQ4MDUwOSA3NS44NDg2MzI4LDQ1Ljk0NzI2NTYgTDc0Ljc2NDY0ODQsNDMuNzUgQzcyLjY3NDc5NDIsMzkuNjA5MzU0MyA3MC4yMDQxMTU4LDM2LjY4OTQ2MTYgNjcuMzUyNTM5MSwzNC45OTAyMzQ0IEM3MC42OTIzOTk1LDMyLjE3NzcyMDMgNzMuNjcwODg1NCwzMC43NzE0ODQ0IDc2LjI4ODA4NTksMzAuNzcxNDg0NCBDNzguMDI2Mzc1OSwzMC43NzE0ODQ0IDc5LjM5MzU0OTcsMzEuNDQwNDIzIDgwLjM4OTY0ODQsMzIuNzc4MzIwMyBDODEuMzg1NzQ3MiwzNC4xMTYyMTc2IDgyLjIzNTM0OCwzNi40MzU1MzA0IDgyLjkzODQ3NjYsMzkuNzM2MzI4MSBDODguMDE2NjI3LDMwLjEwNzM3MzcgOTEuOTcxNjY1NSwyMy40NTcwNDk2IDk0LjgwMzcxMDksMTkuNzg1MTU2MiBDOTcuMzQyNzg2MSwxNi40ODQzNTg1IDk5LjUxNTYxNiwxNC4yOTY4ODA0IDEwMS4zMjIyNjYsMTMuMjIyNjU2MiBDMTAzLjEyODkxNSwxMi4xNDg0MzIxIDEwNS41MjYzNTIsMTEuNjExMzI4MSAxMDguNTE0NjQ4LDExLjYxMTMyODEgQzExMC4zODk2NTgsMTEuNjExMzI4MSAxMTIuNDIwODg3LDExLjk3MjY1MjYgMTE0LjYwODM5OCwxMi42OTUzMTI1IEMxMDkuNTEwNzE3LDE2LjAzNTE3MjkgMTAzLjk0OTI0OSwyMS41MjMzOTkzIDk3LjkyMzgyODEsMjkuMTYwMTU2MiBDOTEuODk4NDA3NCwzNi43OTY5MTMyIDg2LjAzNDIwODIsNDUuNzcxNDMyOCA4MC4zMzEwNTQ3LDU2LjA4Mzk4NDQgWiIgaWQ9IuKckyIgZmlsbD0iI0ZGRkZGRiIgZmlsbC1ydWxlPSJub256ZXJvIj48L3BhdGg+PC9nPjwvc3ZnPg==');
background-position: top right;
background-repeat: no-repeat;
background-size: contain;
background-color: #eeffee;
}
What the CSS parameters do:
background-image
sets the background to the inline image. You can copy and paste the content into the Base64Decoder to get back the original imagebackground-position
puts the image in the upper right corner. You could put it into the left corner when you mirror itbackground-repeat
ensures that the image gets painted once onlybackground-color
isn't needed for the image, it is just to make clearer what exactly got selected. You might opt for aborder
property insteadbackground-size
defines the size of the image. SVG by nature doesn't have a size and it will fill the available height of the container. In conjunction with the viewbox value,contain
will hence show the image exactly on half of div height. This might not work for all use cases. When your container is very long, you get a big big big check mark. There you would specify a discrete size
Enjoy! As usual YMMV!
Posted by Stephan H Wissel on 18 April 2019 | Comments (0) | categories: Lightning Salesforce