DEV Community

Cover image for Bringing Icons to Life in Vue.js with the Dynamic 'vivid-icon' Component
Vishnuraj Sivakumar
Vishnuraj Sivakumar

Posted on

Bringing Icons to Life in Vue.js with the Dynamic 'vivid-icon' Component

Are you building a Vue.js application and looking for a way to easily display and customize icons? Look no further than the "vivid-icon" component.

Before diving into the implementation of the "vivid-icon" component you need to understand how to import svg in vue+vuetify. you can find the official documentation from vuetify here

The "vivid-icon" component is based on Vue's built-in "v-icon" component, but extends its functionality by allowing you to customize the color, fill, and stroke of the icon. This is achieved through the use of props:

vivid-icon were written on top of the v-icon (a vuetify component) to support both icon and svg.

<template>
    <v-icon v-bind="$attrs" v-on="$listeners" :color="color"
        :class="computedFill || computedStroke || computedColor ? `vivid-icon__f${computedFill}_s${computedStroke}_c${computedColor} vivid-icon` : ''">
        <slot />
    </v-icon>
</template>
Enter fullscreen mode Exit fullscreen mode
props: {
    color: { default: '' },
    fill: { default: '' },
    stroke: { default: '' },
},
Enter fullscreen mode Exit fullscreen mode

But how exactly does this component change the appearance of the icon? The component uses two utility functions, createCssVariableScoped() and updateCssVariablesScoped() to create and update CSS variables that are used to style the icon. These functions are defined in the script section of the component, and allow the component to dynamically update the appearance of the icon based on the values of its props.

The createCssVariableScoped() function takes four arguments: properties, cssClass, element, and additionalClass. It creates a new style tag, sets its id attribute to the concatenation of the cssClass and the componentId, and sets its type attribute to "text/css". It then creates a new style using properties and additionalClass and sets it as the innerText of the style tag. Finally, it returns the style tag.

function createCssVariableScoped(
    properties,
    cssClass,
    element,
    additonalClass
) {
    let componentId = element.attributes[0].name
    let styleTag = document.createElement('style')
    styleTag.id = cssClass + componentId
    styleTag.setAttribute('type', 'text/css')
    let style = `.${cssClass}${additonalClass}{
           ${properties}
          }`

    styleTag.innerText = style
    return styleTag
}
Enter fullscreen mode Exit fullscreen mode

The updateCssVariablesScoped() function takes four arguments: updatedStyle, cssClass, element, and additionalClass. It first retrieves the componentId from the element attributes. It then creates a new style using updatedStyle and additionalClass and sets it as the innerText of the style tag with the id of cssClass + componentId. If the tag is already present it will remove the existing one.

function updateCssVariablesScoped(
    updatedStyle,
    cssClass,
    element,
    additonalClass
) {
    let componentId = element.attributes[0].name
    let style = `.${cssClass}${additonalClass} {
        ${updatedStyle}
    }`

    document
        .querySelectorAll('#' + cssClass + componentId)
        .forEach((item, length) => {
            if (length > 0) {
                item.remove()
            }
        })
    document.getElementById(cssClass + componentId).innerText = style
}
Enter fullscreen mode Exit fullscreen mode

And are used in the component's "mounted" lifecycle hook to create a style tag that contains the CSS variables and to update the variables when the "color", "fill", or "stroke" props change:

mounted() {
        if (this.stroke || this.fill || this.color) {
            let props = `stroke: ${this.stroke ? this.stroke : "inherit"};fill: ${this.fill ? this.fill : "inherit"};color: ${this.color ? this.color : "inherit"};`;

            let additionalClass = `.vivid-icon__f${this.computedFill}_s${this.computedStroke}_c${this.computedColor} .vivid-icon--color`;


            this.styleTag = createCssVariableScoped(
                props,
                "vivid-icon",
                this.$el,
                additionalClass
            );
            document.head.appendChild(this.styleTag);
        }
    }
Enter fullscreen mode Exit fullscreen mode

In addition to this, the component also uses computed properties to convert the "color", "fill", and "stroke" props to strings, which are used to generate class names for the icon. These class names are used to scope the CSS variables to the icon, so that they only affect the icon and not other elements on the page:

computed: {
  computedColor() {
    return convertToStr(this.color);
  },
  computedFill() {
    return convertToStr(this.fill);
  },
  computedStroke() {
    return convertToStr(this.stroke);
  },
}
Enter fullscreen mode Exit fullscreen mode

The convertToStr function inside the computed convert the multiple color type input to common standard and that color is used to apply in the icon.

function convertToStr(str) {
    if (!str) {
        return "";
    }
    if (str.startsWith("#")) {
        return str.substring(1);
    }
    if (str.startsWith("rgba")) {
        return str.substring(5, str.length - 1);
    }
    if (str.startsWith("rgb")) {
        return str.substring(4, str.length - 1);
    }
    if (str.startsWith("var")) {
        return str.substring(4, str.length - 1);
    }
}
Enter fullscreen mode Exit fullscreen mode

The "watch" property is an important feature of the "vivid-icon" component, as it ensures that the icon's appearance is updated in real-time as the "color", "fill" and "stroke" props change. This provides a dynamic and interactive experience for the user, making it easy for them to see the changes they make to the icon's appearance.

In addition to the "watch" property, the component also has a "beforeDestroy" hook that removes the component from the DOM and the component's style tag from the head of the document when the component is destroyed:

beforeDestroy() {
   this.styleTag.remove()
   this.$el.remove()
},
Enter fullscreen mode Exit fullscreen mode

This ensures that the component's resources are properly cleaned up when it is no longer needed, which is important for maintaining the performance of the application.

To use the "vivid-icon" component in your Vue.js application, you simply need to import it and use it like any other Vue component. For example:

<template>
    <vivid-icon :color="color" :fill="fill" :stroke="stroke"> $vue</vivid-icon >
</template>

<script>
import vividIcon from '../components/vividIcon.vue'

export default {
    components: {
        vividIcon,
    },
    data() {
        return {
            color: 'red',
            fill: 'blue',
            stroke: 'green',
        }
    },
}
</script>
Enter fullscreen mode Exit fullscreen mode

In this example, the "vivid-icon" component is used with the "color", "fill" and "stroke" props set to "red", "blue", and "green" respectively. This would result in an icon that has a red color, blue fill, and green stroke.

To implement the "vivid-icon" component in your Vue.js application, you will need to follow these steps:

  • Download the SVG file that you want to use as an icon.
  • Rename the file from .svg to .vue.
  • This will allow you to use the file as a Vue component.
  • Wrap the content of the SVG file with a tag. This will ensure that the SVG content is properly rendered as an icon.
  • Create an index file and add the component as shown in the example below:
import vue from './vue';

export default {
  vue: {
    component: vue,
  },
}
Enter fullscreen mode Exit fullscreen mode
  • Import that in vuetify plugin as shown in the example below:
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import IconValue  from './index.js'

Vue.use(Vuetify)

export default new Vuetify({
    icons: {
        iconfont: 'mdi', // default - only for display purposes
        values: IconValue,
    },
})
Enter fullscreen mode Exit fullscreen mode

You can now use the icon in your template by calling the component, passing in the props of size, fill, and stroke, and the class vivid-icon--color as shown in the example below:

<template>
    <vivid-icon size="45" fill="orange">$vue</vivid-icon>
</template>
<script>
import VividIcon from '../components/vividIcon.vue'
export default {
    components: {
        VividIcon,
    },
}
</script>
Enter fullscreen mode Exit fullscreen mode

By following these steps, you will be able to easily use and customize icons in your Vue.js application using the "vivid-icon" component. The ability to import svg in vue+vuetify allows you to have a wide range of icons to choose from and customize as per your requirement. This component is easy to use, and it allows you to change the color, fill, and stroke of your icons and ensure that the changes are only applied to specific elements.

In conclusion, the "vivid-icon" component is a powerful and flexible way to display and customize icons in a Vue.js application. With the ability to customize the color, fill, and stroke of the icon and the use of computed properties and the watch property, it provides a dynamic and interactive experience for the user. It is easy to use, and it is a great way to add a touch of visual appeal to your application.

Source code: vivid-icon-vue

Top comments (0)