If you can't append/insert svg elements you might use mask-image
instead (still needs browser-prefixes!).
The actual fill color is set by background-color
property:
.myIcon {
-webkit-mask-image: var(--icon-arrowDownUrl);
mask-image: var(--icon-arrowDownUrl);
background-color: red;
}
Example using mask-image
:root {
--icon-arrowDownUrl: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpolygon points='50 60 0 0 100 0 50 60'/%3E%3C/svg%3E");
}
.myIcon {
width: 2rem;
height: 2rem;
-webkit-mask-image: var(--icon-arrowDownUrl);
mask-image: var(--icon-arrowDownUrl);
-webkit-mask-size: contain;
mask-size: contain;
background-color: red;
background-repeat: no-repeat;
transition: 0.3s;
}
.myIcon:hover {
background-color: green;
}
/* select example */
.select-wrp {
width: 25%;
margin-bottom: 2em;
}
.select-wrp * {
display: block;
width: 100%;
font-size: inherit;
}
.select-wrp {
width: 25%;
margin-bottom: 2em;
position: relative;
}
.select-wrp * {
display: block;
width: 100%;
font-size: inherit;
}
.select-custom-icon {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.select-wrp:after {
content: "";
display: block;
width: 1em;
height: 1em;
-webkit-mask-image: var(--icon-arrowDownUrl);
mask-image: var(--icon-arrowDownUrl);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: 100% 50%;
mask-position: 100% 50%;
position: absolute;
right: 0.5em;
top: 25%;
z-index: 10;
background-color: red;
pointer-events: none;
}
.select-wrp:hover:after {
background-color: green;
}
<div class="myIcon"></div>
<div class="select-wrp">
<select class="select-custom-icon">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
<option>Option 6</option>
</select>
</div>
Common pitfalls:
Use an additional element (e.g a pseudo element with a css content
) if you need to add icons to parent tags like <select>
otherwise you'll mask the whole parent element (e.g. the select field would be invisible). See example snippet.
Inlined svg – bloated code?
As @CBroe already mentioned: appending your icons as reusable assets is certainly the most versatile and powerful approach when it comes to css styling and dynamic content/shape manipulations (e.g via js).
But there are also 'semi-stylable' approaches:
Most notably, you can also add icons to your html via <use>
elements referenced from an external file like so:
<svg class="myIcon" viewBox='0 0 100 100'>
<use href="icons.svg#arrowDown" />
</svg>
Granted your svg source is stored on the same domain!
Admittedly, this concept is not at all on par with inlined svg i.e. externally referenced <use>
elements are lacking some styling abilities (or at least not supported by a vast majority of browsers) like:
- filters
- clip-paths/masks
- patterns
- ... and probably other features
Example: external <use>
href (emulated):
<!--emulate external svg file content -->
const pseudoExtSvgs = document.querySelectorAll('.pseudoExternal');
showPseudoExternal(pseudoExtSvgs);
function showPseudoExternal(els){
els.forEach(function(item, i){
let type = item.nodeName.toLowerCase();
let svgEl = type=='img' ? item : item.querySelector('use');
let svgSrc = type=='img' ? item.getAttribute('src') : item.querySelector('use').getAttribute('href') ;
let svgId = svgSrc.split('.svg')[0];
let svgTarget = svgSrc.indexOf('#')!==-1 ? '#'+svgSrc.split('#').pop() : '';
let svg = document.querySelector('#'+svgId);
if(svg){
if(type=='img'){
let dataUrl = svg2DataUrl(svg)+svgTarget;
svgEl.src = dataUrl;
}
if(type=='svg'){
svgEl.setAttribute('href', svgTarget);
}
}
});
}
.myIcon {
width: 2rem;
height: 2rem;
transition: 0.3s;
color: red;
}
.myIcon:hover,
.select-wrp:hover .myIcon {
color: green;
}
/* select example */
.select-wrp {
width: 25%;
position: relative;
}
select {
width: 100%;
display: block;
appearance: none;
}
.select-wrp .myIcon {
display: block;
position: absolute;
width: 0.75em;
height: 0.75em;
position: absolute;
right: 0.5em;
top: 25%;
z-index: 10;
pointer-events: none;
}
<div class="myIcon">
<svg viewBox='0 0 100 100' class="pseudoExternal">
<use href="icons.svg#arrowDown" />
</svg>
</div>
<div class="select-wrp">
<select class="select-custom-icon">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
<option>Option 6</option>
</select>
<svg class="myIcon pseudoExternal" viewBox='0 0 100 100'>
<use href="icons.svg#arrowDown" />
</svg>
</div>
<!--emulated icons.svg file content -->
<svg class="svgAsset" id="icons" style="width:0; height:0; position:absolute;visibility:hidden;" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
<symbol id="arrowDown">
<polygon fill="currentColor" points='50 60 0 0 100 0 50 60' />
</symbol>
</svg>
(Js part is only for emulating an external file source – test it on your local server, webspace etc)
However, the ability to avoid redundant svg code in your svg html body is certainly handy for simple reusable assets like icons.
In this case, changing some more basic properties like fill, color stroke-width etc. might be sufficient.