Search code examples
vuetify.jsvuetifyjs3

Making Vuetify (3) select dropdowns stand out against card/dialog surfaces


I have a VDialog containing a VCard which in turn contains a VSelect. When I open the VSelect, it is hard to see the drop-down list of options as it has the same color as the VCard. Both seem to have background: rgb(var(--v-theme-surface)); The only thing making the dropdown stand out is a shadow, but in a dark-theme, you can barely see it.

Example of a drop-down not standing out

What is the recommended way to change the background of a VSelect (or VMenu, VCombobox) if it is inside a VDialog or VCard?

I know Vuetify 3 has a way to change global config (including nested options, like a VSelect inside a VCard) but I could not get that to work.

I also tried with CSS variables, changing the value of the --v-theme-surface CSS property, but it looks like the dropdown is moved outside of the parent container and put close to the html <body>, so I can't target it.

Is there a way to 'send in' a CSS class into the child drop down menu so I can target it that way?

Many thanks.


Solution

  • I am not aware of a way to change styles of nested components through Vuetify directly, but using the :menu-props property, you can pass a configuration object to the underlying v-menu component. The content of the :content-class property will be set as class on the overlay content node:

    <v-select
      ...
      :menu-props="{
        contentClass: 'lighter-surface',
      }"
    >
    

    With this, you can now define a CSS rule to adjust the background color variable:

    .lighter-surface > .v-theme--dark{
      --v-theme-surface: var(--v-theme-on-surface-variant); /* or something like 55,55,55; */
    }
    

    const { createApp } = Vue;
    const { createVuetify } = Vuetify
    const vuetify = createVuetify({theme: {defaultTheme: 'dark'}})
    const app = {}
    createApp(app).use(vuetify).mount('#app')
    .lighter-surface > .v-theme--dark{
      --v-theme-surface: var(--v-theme-on-surface-variant);
    }
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" />
    <link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
    <div id="app">
      <v-app>
        <v-main>
          <v-card class="ma-4">
            <v-card-title>Selection Card</v-card-title>
            <v-card-text>
              <v-select
                :items="['option 1', 'option 2', 'option 3']"
                label="Select"
                :menu-props="{
                  contentClass: 'lighter-surface',
                }"
              ></v-select>
              <div style="height: 200px;">
              </div>
            </v-card-text>
          </v-card>
        </v-main>
      </v-app>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>

    If you don't want to change global styles, you can try to attach the v-menu to the v-card, using the :attach property, then you could work with local styles. Not sure if it's worth it though.


    Or you can solve it by adding your own theme:

    const vuetify = createVuetify({
      theme: {
        defaultTheme: 'dark',
        themes: {
          darkOnSurface: { // <--- name of the theme
            dark: true, // <--- extend dark theme
            colors: {
              surface: 'blue', // <--- override color variant from dark theme
            }
          }
        }
      }
    })
    

    And then set that theme in the v-menu:

    <v-select
      ...
      :menu-props="{
        theme: 'darkOnSurface',
      }"
    ></v-select>
    

    The advantage is that you can use it on all Vuetify components.

    const {
      createApp
    } = Vue;
    const {
      createVuetify
    } = Vuetify
    const vuetify = createVuetify({
      theme: {
        defaultTheme: 'dark',
        themes: {
          darkOnSurface: {
            dark: true,
            colors: {
              surface: 'blue',
            }
          }
        }
      }
    })
    const app = {}
    createApp(app).use(vuetify).mount('#app')
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" />
    <link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
    <div id="app">
      <v-app>
        <v-main>
          <v-card class="ma-4">
            <v-card-title>Selection Card</v-card-title>
            <v-card-text>
              <v-select
                :items="['option 1', 'option 2', 'option 3']"
                label="Select"
                :menu-props="{
                  theme: 'darkOnSurface',
                }"
              ></v-select>
              <div style="height: 200px;">
              </div>
            </v-card-text>
          </v-card>
        </v-main>
      </v-app>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>