vuetifyjs / vuetify

🐉 Vue Component Framework
https://vuetifyjs.com
MIT License
39.71k stars 6.95k forks source link

[Feature Request] Allow multiple themes #10985

Closed Soletiq closed 3 years ago

Soletiq commented 4 years ago

Problem to solve

Currently, it seems I can only create 1 light and dark theme. But I want the capability of being able to create multiple themes each with their own light and dark variants.

Proposed solution

Allow us to provide an array of themes and select which theme is active.

MajesticPotatoe commented 4 years ago

Thought about doing something for this in v3, though there is already some overlap of being able to do this in userland naturally, as well as through presets.

billsliu commented 4 years ago

Vuetify most of time used to create Admin application, light theme is well enough to most company, Vuetify should concentrate on its core.

MohawkMan commented 4 years ago

@Soletiq I keep an array of themes in my store and switch between them with a simple mutation and Object.assign

sugoidesune commented 4 years ago

I would also like to have multiple themes, I heavily customize colors and override the sass variables. And Themes would be used for different parts of the app. blue theme - selling green theme - buying yellow - search

i found this mixin in the src but im not sure how to override it from outside and make it more programmatic.

` @mixin theme ($component) .theme--light.#{$component} @content($material-light) .theme--dark.#{$component} @content($material-dark)

`

Would be very thankful if someone could point me to a way in doing that if possible

johnleider commented 3 years ago

This is happening implicity for version 3. Please follow https://github.com/vuetifyjs/vuetify/issues/12688 for updates.

If you have any additional questions, please reach out to us in our Discord community.

jaythomas commented 3 years ago

I know this issue is closed but it still comes up in search engine results, so for anyone else looking to implement such a thing here's my code.

In plugins/vuetify.js I store my extra themes, but really you could store these anywhere in your app that makes sense to you:

import Vue from 'vue'
import Vuetify from 'vuetify/lib/framework'

Vue.use(Vuetify)

export default new Vuetify({
  theme: {
    options: { customProperties: true },
    themes: {
      blue: {
        accent: '#82B1FF',
        error: '#FF5252',
        info: '#2196F3',
        primary: '#1976D2',
        secondary: '#424242',
        success: '#4CAF50',
        warning: '#f88C00'
      },
      green: {
        accent: '#CDDC39',
        error: '#ff5722',
        info: '#1976D2',
        primary: '#4caf50',
        secondary: '#009688',
        success: '#1976D2',
        warning: '#ff9800'
      }
    }
  }
})

In plugins/store.js I made a mutation that applies the theme when they're set in the store:

import Vue from 'vue'
import Vuex from 'vuex'
// Importing the vuetify instantiation, not the Vuetify framework itself
import vuetify from '@/plugins/vuetify'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    preferencesDrawer: false,
    preferences: {
      contrast: 'light',
      theme: 'blue'
    }
  },
  mutations: {
    // Same theme is used in a light and dark variant, e.g. light green and dark green
    setTheme(state, theme) {
      Vue.set(state.preferences, 'theme', theme)
      vuetify.framework.theme.themes.dark = vuetify.userPreset.theme.themes[theme]
      vuetify.framework.theme.themes.light = vuetify.userPreset.theme.themes[theme]
    },
    // Now you can switch between the light and dark version of the green theme
    setContrast(state, contrast) {
      Vue.set(state.preferences, 'contrast', contrast)
      vuetify.framework.theme.dark = contrast === 'dark'
    }
  }
})

And then somewhere in the UI you can interface with the store. PreferencesDrawer.vue:

<template>
  <v-navigation-drawer v-model="open" fixed right temporary>
    <v-list-item>
      <v-select label="color theme" :items="themeOptions" v-model="theme" />
    </v-list-item>
    <v-list-item>
      <v-select label="contrast" :items="contrastOptions" v-model="contrast" />
    </v-list-item>
  </v-navigation-drawer>
</template>

<script>
export default {
  data() {
    return {
      themeOptions: [
        {
          text: 'blue',
          value: 'blue'
        },
        {
          text: 'green',
          value: 'green'
        }
      ],
      contrastOptions: [
        {
          text: 'dark',
          value: 'dark'
        },
        {
          text: 'light',
          value: 'light'
        }
      ]
    }
  },
  computed: {
    // Open/close this whole drawer menu
    open: {
      get() {
        return this.$store.state.preferencesDrawer
      },
      set(value) {
        this.$store.commit('setPreferencesDrawer', value)
      }
    },
    theme: {
      get() {
        return this.$store.state.preferences.theme
      },
      set(theme) {
        this.$store.commit('setTheme', theme)
      }
    },
    contrast: {
      get() {
        return this.$store.state.preferences.contrast
      },
      set(contrast) {
        this.$store.commit('setContrast', contrast)
      }
    }
  }
}
</script>

Cheers.

matthieusieben commented 2 years ago

Here is a workaround fir Vuetify 2 allowing to use a custom theme locally:

<template>

<div :class="{ [className]: true }">
    <style v-if="css">{{ css }}</style>
    <slot name="default"></slot>
</div>

</template>

<script>
import Vue from 'vue'
import { genStyles, parse } from 'vuetify/lib/services/theme/utils'

export default Vue.extend({
  name: 'x-custom-theme',

  props: {
    colors: { type: Object, default: () => null },
  },

  computed: {
    className() {
      return `x-custom-theme--theme-${this._uid}`
    },
    css() {
      if (!this.colors) return null
      try {
        const { primary, anchor } = this.$vuetify.theme.currentTheme
        const theme = { primary, anchor, ...this.colors }
        return genStyles(parse(theme)).replaceAll(
          '.v-application ',
          `.v-application .${this.className} `
        )
      } catch (err) {
        console.warn(`Failed to generate local theme:`, err)
        return null
      }
    },
  },
})
</script>

You can use this as:

<x-custom-theme :colors="{ primary: '#ffeeaaa' }">
  <v-btn color="primary">Click here</v-btn>
</x-custom-theme>

Note this is quite hackish ;-) (e.g. $vuetify.theme.currentTheme will not have the right value within the component).