FortAwesome / Font-Awesome

The iconic SVG, font, and CSS toolkit
https://fontawesome.com
Other
73.49k stars 12.2k forks source link

Gradients on FA v5 icons #11925

Open jebarjonet opened 6 years ago

jebarjonet commented 6 years ago

I liked the fact that FA < v4 was a font so that I could apply a CSS gradient on it (ex: http://jsfiddle.net/HGxMu/220/)

screen shot 2017-12-13 at 09 49 36
.icon {
    font-size: 50px;
    background: -webkit-gradient(linear, left top, left bottom, from(#0c8), to(#333));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

I am currently using react-fontawesome which outputs SVG icons, on which I can not apply CSS gradients. Is there a way to either:

tagliala commented 6 years ago

Hi,

I'm not sto much into SVG icons and javascript.

I've found this workaround on SO: https://jsfiddle.net/tagliala/f019sa7v/2/

Ref: #https://stackoverflow.com/questions/10894377/dynamically-adding-a-svg-gradient

Maybe it is possible to add a feature to use icons as clippath or add extra properties to the generated svg

jebarjonet commented 6 years ago

@tagliala the jsfiddle example is not working on my end (MacOS / Chrome v63) but I believe that SVG gradients definitions with Javascript are a real pain. Even if I could pass a <LinearGradient> inside the <svg> and reference it in the <path> I would still have to generate it with React which I prefer to avoid :)

The cleanest -and pure CSS- way would be that I could use FA web font instead of SVG icons

ccprog commented 6 years ago

I just spelled out a static solution on SO: https://stackoverflow.com/a/47801536/4996779

galtalmor commented 6 years ago

@ccprog Thanks, your solution works like a charm. Still, it would have been awesome if this was a part of FA's API.

jebarjonet commented 6 years ago

Note that this is not running with React in this demo 🙂 My solution was to use the Font version of FA 5 (instead of the SVG version) so I could use CSS gradients on it instead of using react-fontawesome

CanRau commented 6 years ago

@jebarjonet maybe I got you wrong(?!), but I'm using @ccprog's solution in react

jebarjonet commented 6 years ago

@CanRau yep, but I really wanted to generate gradients on the fly as simply as a css line

CanRau commented 6 years ago

@jebarjonet would definitely be preferable 👍

r-tanner-f commented 6 years ago

Having a hard time with @ccprog's solution using Vue and with webpack. I managed to get webpack to inline the svg in the url() but the gradient never displayed. Not sure what I'm doing wrong.

CanRau commented 6 years ago

@r-tanner-f have you tried to preserve the reference instead of inlining it in the css like in ccprog's answer? i don't know but it might not work like this.. so you have the svg in you html, hidden with css and the css just references the id of the svg gradient

stavvie34 commented 6 years ago

For anyone still trying to achieve this effect with Font Awesome (5.0.13 as of this comment) working with JS and SVG, @ccprog 's solution above worked for me.

I had several icons that I wanted to apply a gradient to; I didn't care about hover, I just wanted it on there.

Here are the steps I followed to achieve this effect:

  1. I used Angry Tools's gradient generator to give me an svg I can define.
  2. Define an svg element in your html.
    <svg width="0" height="0">
    <linearGradient id="lgrad" x1="100%" y1="100%" x2="0%" y2="0%" >
        <stop offset="0%" style="stop-color:rgb(252,207,49);stop-opacity:1" />
        <stop offset="100%" style="stop-color:rgb(245,85,85);stop-opacity:1" />
    </linearGradient>
    </svg>
  3. In your stylesheet, reference the svg icons you would like to apply the gradient to. With Font Awesome 5.0.13, using js, your <i> tags are replaced with inline-svg.
    .parent-div svg * {
    fill: url(#lgrad);
    }

Done!

See this JSFiddle for an example.

the94air commented 5 years ago

Thanks, @stavvie34 .This will get you into troubles inside of a loop because you can't use the same id for multiple elements. You can't use v-bind:style here because we need to have the same id in both CSS and SVG. Here an example of what I could do hoping it will help someone. Source

<template>
    <!-- ... -->
    <Fa :icon="[ 'fab', child.icon ]" />
    <svg width="0" height="0">
        <linearGradient :id="'lgrad' + child.id" x1="100%" y1="100%" x2="0%" y2="0%">
            <stop ... />
            <stop ... />
        </linearGradient>
    </svg>
    <!-- ... -->
</template>

<script>
    export default {
        // Our prop
        // child: {
        //     id: 4,
        //     icon: 'js',
        // },
        mounted() {
            let style = document.createElement('style');
            style.type = "text/css";
            style.appendChild(document.createTextNode(''));
            this.styleNode = style.childNodes[0];
            document.head.appendChild(style);

            // The magic
            let css = '.fa-' + this.lang.icon + ' * {fill: url(#lgrad' + this.child.id + ');}';
            this.styleNode.textContent = css;
        },
        props: {
            child: Object,
        },
    }
</script>

I guess it can also be done using CSS variable but I could't find a way to get the prop inside the style scope.

stychu commented 5 years ago

@stavvie34 Amazing thanks! You saved me <3

seanreiser commented 5 years ago

Have you considered using a mask to accomplish this? Here's a quick codepen demoing what I;m thinking.

https://codepen.io/seanreiser/pen/QWLaZZp

NastyDogBreath commented 4 years ago

Have you considered using a mask to accomplish this? Here's a quick codepen demoing what I;m thinking.

https://codepen.io/seanreiser/pen/QWLaZZp

Thank You!

ghost commented 4 years ago

Have you considered using a mask to accomplish this? Here's a quick codepen demoing what I;m thinking.

https://codepen.io/seanreiser/pen/QWLaZZp

In case it helps anyone (it took me a while to find) to get this working in angular-fontawesome you need to add a custom class: https://github.com/FortAwesome/angular-fontawesome/issues/173#issuecomment-529375797

sylvainDNS commented 3 years ago

I don't like these solutions because I am working with <FontAwesomeIcon /> React component and I don't want to work with custom svg tags and components...

I found a workaround for MY problem : image

To achieve that, I am using FontAwesome layers & mask :

<span className="fa-layers fa-fw">
  <FontAwesomeIcon
    className="gradient"
    icon="circle"
    mask="square-full"
    color="white"
  />
  <FontAwesomeIcon
    icon="check"
    inverse
    transform="shrink-6"
  />
</span>
.gradient {
  background: linear-gradient(
    180deg,
    rgba(94, 189, 62, 1) 34%,
    rgba(255, 185, 0, 1),
    rgba(247, 130, 0, 1),
    rgba(226, 56, 56, 1),
    rgba(151, 57, 153, 1),
    rgba(0, 156, 223, 1) 91%
  );
}

Here is a live demo : https://codepen.io/SylvainDNS/pen/ZEpXEEZ

temmiland commented 2 months ago

Hi guys, when using vh or vw units, the solution described above often leads to display errors in the browser. After hours of despair I solved it ugly with the following code:

const iconRef = useRef < HTMLDivElement | null > (null);

useEffect(() => {
    const svg = iconRef.current!.querySelector('svg');
    if (svg) {
        const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
        const linearGradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
        linearGradient.setAttribute('id', 'pride');
        linearGradient.setAttribute('x1', '0%');
        linearGradient.setAttribute('y1', '0%');
        linearGradient.setAttribute('x2', '0%');
        linearGradient.setAttribute('y2', '100%');

        const stops = [
            {
                offset: '0%',
                color: '#ff0018'
            },
            {
                offset: '16.7%',
                color: '#ff0018'
            },
            {
                offset: '16.7%',
                color: '#ff8c00'
            },
            {
                offset: '33.3%',
                color: '#ff8c00'
            },
            {
                offset: '33.3%',
                color: '#ffed00'
            },
            {
                offset: '50%',
                color: '#ffed00'
            },
            {
                offset: '50%',
                color: '#008026'
            },
            {
                offset: '66.7%',
                color: '#008026'
            },
            {
                offset: '66.7%',
                color: '#0000f9'
            },
            {
                offset: '83.3%',
                color: '#0000f9'
            },
            {
                offset: '83.3%',
                color: '#a700ff'
            },
            {
                offset: '100%',
                color: '#a700ff'
            }
        ];

        stops.forEach(({
            offset,
            color
        }) => {
            const stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
            stop.setAttribute('offset', offset);
            stop.setAttribute('stop-color', color);
            linearGradient.appendChild(stop);
        });

        defs.appendChild(linearGradient);
        svg.prepend(defs);

        const path = svg.querySelector('path');
        if (path) {
            path.setAttribute('fill', 'url(#pride)');
        }
    }
}, []);

return (
    <span ref={ iconRef } >
        <FontAwesomeIcon icon={ faHeart } />
    </span>
);

Result:

result