Best way to dynamically change theme of my Vue.js SPA?

Eugenio picture Eugenio · Mar 29, 2018 · Viewed 9.9k times · Source

So I think the title is enough explaination: I would like to dynamically theme my whole application. Maybe this means to change color of all divs when I press a specific button, or change the whole webapp's colors when a specific user logs in.

Just to give some insights on what I am currently working on I will say that I have built a Vue.js app that uses many libraries, including one called Element-ui which already has a theming option built onto it. The problem is that it's written in scss and I would like to change all the variable colors during the navigation. My project looks something like this:

<template>
  ... some HTML and components...
</template>

<script>
  ... some javascript ...
</script>

<style scoped>
  ... some style that is scoped to the current component only ...
</style>

I have many files like this one so making a "global function" for all of them doesn't seem practical to me. Also I import the main scss file just once in my main.js.

Is there anything I can do to create a dinamic theming for my webapp? Is using saas a good idea? Javascript maybe?

EDIT

I feel like I didn't explain it good enough so I want to add a simple example. If you visit the Element page you can see in the top right corner there is a color selector that, when a color changes, it changes also the whole website's accent colors like the buttons colors, the links colors etc.

Hope this can help understanding a bit better

EDIT

Right now I have settled on a very poor and, I think, badly optimized solution. The idea is that when the user changes theme, I just create a new css file and append it to the current document.

let sheet = document.createElement('style')
sheet.innerHTML = `*[class*="--primary"]{
                      background-color: ${colors[0]};
                    }
                    ...`;
document.body.appendChild(sheet);

I truly think this is a very bad solution but right now I can't come up with nothing else that could work dinamically when the user changes a parameter. I would really want the process to be flawless: the user picks a color and the whole application just changes to that specific color, no prebuilt theme.css.

FINAL EDIT

I've finally found a solution, refer to the answer!

Answer

Eugenio picture Eugenio · Apr 4, 2018

Finally, after a few long days I came up with a solution that I think is both easy to implement and very very lightweight.

CSS VARIABLES

Before searching up a lot, I didn't even know the existance of these kind of variables and they don't seem so used. Anyway, I hope this can help someone out there seeking my same answer:

<template
  <app-main></app-main>
  <app-sidebar></app-sidebar>
  ...
</template>

<style>
  :root{
    --primary-color: #C5C5C5!important;
    --secondary-color: #6C7478!important;
    --tertiary-color: #FFFFFF!important;
    --success-color: #80b855!important;
    --warning-color: #eaca44!important;
    --error-color: #ef4d4d!important;
  }

  /* Theming */
  header{
    background-color: var(--primary-color);
  }
  div{
    color: var(--tetriary-colory);
  }
  ...
  /*   END   */
</style>

<script>
  import axios from 'axios' /* all your imports etc. */

  export default{
    data(){
    },
    methods: {
      axios.post(`http://localhost:8080/foo`).then(function (response){
        let bodyStyles = document.body.style;
        bodyStyles.setProperty('--primary-color', response.colors[0]);
        bodyStyles.setProperty('--tertiary-color', response.colors[1]);
        ...
      }
    }
  }
</script>

As you can see, I just initialize a few useful CSS variables and when I need them (for example in that api post call) I just modify them using a simple bodyStyles.setProperty('propertyName') function.

I really enjoy this type of setup since I use it in my login page so when a user successfully logs-in I load from the database his own colors and set them up just like that.

My website example

Hoping this can help someone! Cheers!