PolymerElements / iron-icon

Element that displays a single icon
https://webcomponents.org/element/PolymerElements/iron-icon
40 stars 39 forks source link

iron-icon color (polymer 2) #118

Open csakaszamok opened 6 years ago

csakaszamok commented 6 years ago

Description

How can I colorize the iron icon (with polymer css variable) if it load an external image (like png, svg)? I set the --iron-icon-fill-color variable in css section, but it seems not works for me.

Expected outcome

red icons

Actual outcome

black icons: image

Live Demo

https://jsfiddle.net/szamok/1mk636zz/7/

Version

Polymer: 2.3.1

KeithHenry commented 6 years ago

This is a limitation of SVGs.

When using an iconset the SVG is embedded in the page and cascades style information. When using a src path the SVG is added in an <img> and gets its own private style tree.

The fix would be for <iron-icon> to treat SVG differently from bitmap images - by embedding the SVG in the page.

The easiest way to do with is with a <use>:

<svg style="fill:var(--iron-icon-fill-color, currentColor)">
    <use href$="[[importPath]]../images/arrow.svg#idForGroup"></use>
</svg>

However, this only works if you have an id="idForGroup" on the SVG <g> that you want. I don't think this works for <iron-icon> as it would have to make assumptions about the content of the SVG. I can't even patch your Fiddle because your SVG doesn't have id properties set.

Put this into a little Polymer component that honours <iron-icon>'s styles:

<dom-module id="svg-icon">
    <template>
        <style>
            :host {
                display: inline-block;
                align-items: center center;
                justify-content: center center;
                position: relative;
                vertical-align: middle;
                width: var(--iron-icon-width, 24px);
                height: var(--iron-icon-height, 24px);
            }

            svg#icon {
                fill: var(--iron-icon-fill-color, currentcolor);
                stroke: var(--iron-icon-stroke-color, none);
                width: 100%;
                height: 100%;
            }

        </style>

        <svg id="icon">
            <svg id="view" preserveAspectRatio="none">
                <use id="iconRef" href$="[[src]]#[[groupId]]" on-load="_setViewBox"></use>
            </svg>
        </svg>
    </template>

    <script>
        // @ts-check

        class SvgIcon extends Polymer.Element {
            static get is() { return 'svg-icon'; }
            static get properties() { return { src: String, groupId: String } }
            _setViewBox(e) {
                const size = this.$.iconRef.getBoundingClientRect();
                this.$.view.setAttribute('viewBox', `0 0 ${size.width} ${size.height}`);
            }
        }

        customElements.define(SvgIcon.is, SvgIcon);
    </script>
</dom-module>

That works, but needs a nasty extra event on load to scale the dynamically loaded <use> content.

Another solution would be to import the SVG content like iconsets are, but it might be easier to just collate your SVG as a new set.