【Vue3+TypeScript+Vuetify+Vite】動的なテーマの切り替え

β版の Vue.js（Vue3）+ TypeScript + Vuetify3 + Vite の構成で、動的にテーマを切り替える方法です。
いわゆるダークモードへの切り替えです。
結論として以下のリンクを辿ればできますが、プロジェクトの作成から手順を置いておきます。

- [最初のViteプロジェクトを生成する](https://ja.vitejs.dev/guide/#最初の-vite-プロジェクトを生成する "最初のViteプロジェクトを生成する")
- [Vuetify 3 Beta#Installation](https://next.vuetifyjs.com/en/getting-started/installation/ "Vuetify 3 Beta#Installation")
- [Vuetify 3 Beta#Changing theme](https://next.vuetifyjs.com/en/features/theme/#changing-theme "Vuetify 3 Beta#Changing theme")

記述時点では以下の構成で行っています。
- vue-cli: 5.0.4
  - [Vuetify 3 Beta#Installation](https://next.vuetifyjs.com/en/getting-started/installation/ "Vuetify 3 Beta#Installation") の注意書きより、Vuetify 3を正しくインストールするためには vue-cli は 5.0 以上にする必要があります。
- vite: 2.9.9
- vue: 3.2.25
- vuetify: 3.0.0-beta
- typescript: 4.5.4

## プロジェクトの作成
Viteプロジェクトを作成します。
```sh
npm create vite@latest
```

プロジェクト名を入力します。
```sh
? Project name: » vue3-ts-vuetify-changing-theme
```

`vue` を選択します。
```sh
? Select a framework: » - Use arrow-keys. Return to submit.
    vanilla                                                
>   vue                                                    
    react                                                  
    preact                                                 
    lit                                                    
    svelte
```

`vue-ts` を選択します。
```sh
? Select a variant: » - Use arrow-keys. Return to submit.
    vue                                                  
>   vue-ts  
```

プロジェクトが作成されました。
```sh
Done. Now run:

  cd vue3-ts-vuetify-changing-theme
  npm install                      
  npm run dev 
```

上記の案内がありますが、先にプロジェクトへ移動してVuetifyを追加します。
```sh
cd vue3-ts-vuetify-changing-theme
vue add vuetify
```

`Vite Preview (Vuetify 3 + Vite)` を選択します。
```sh
✔  Successfully installed plugin: vue-cli-plugin-vuetify

? Choose a preset:                
  Configure (advanced)            
  Default (recommended)           
> Vite Preview (Vuetify 3 + Vite) 
  Prototype (rapid development)   
  Vuetify 3 Preview (Vuetify 3)  
```

これで完了です。
```console
✔  Successfully invoked generator for plugin: vue-cli-plugin-vuetify
```

サーバを起動します。
```sh
npm run dev
```

表示の通り `http://localhost:3000/` へアクセスします。
```sh
> vue3-ts-vuetify-changing-theme@0.0.0 dev
> vite


  vite v2.9.9 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

  ready in 378ms.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422235/7260825e-ed24-29df-e20b-0f1046ca0533.png)

## テーマの動的な切り替え
この時点で App.vue は以下の状態です。
```vue:App.vue
<template>
  <v-app>
    <v-main>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export default defineComponent({
  name: 'App',

  components: {
    HelloWorld,
  },

  data () {
    return {
      //
    }
  },
})
</script>
```

[Vuetify 3 Beta#Changing theme](https://next.vuetifyjs.com/en/features/theme/#changing-theme "Vuetify 3 Beta#Changing theme") を参考に修正します。
リンク先とは異なり、以下は [script setup](https://v3.ja.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) で書いています。

```vue:App.vue
<template>
  <v-app :theme="theme">
    <v-main>
      <v-btn @click="toggleTheme">toggle theme</v-btn>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>

<script setup lang="ts">
import {ref} from 'vue'
import HelloWorld from './components/HelloWorld.vue'

const theme = ref('light')
const toggleTheme = () => theme.value = theme.value === 'light' ? 'dark' : 'light'
</script>
```

ボタンで動的にテーマを切り替えられるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422235/8cd9c96f-a83d-fa64-3d39-ec29d54a26bc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422235/1694db0b-0dcf-aa84-bf7c-8849fc9c6577.png)

## テーマの更新

新しいテーマの追加や、既存の light 、 dark テーマの更新は [Vuetify 3 Beta#Setup](https://next.vuetifyjs.com/en/features/theme/#setup "Vuetify 3 Beta#Setup") に記載されています。

例えば以下のように light テーマを更新すると、背景色やボタンの色が更新されます。

```typescript:vuetify.ts
import { createVuetify } from 'vuetify'

const light = {
    dark: false,
    colors: {
        background: '#808080',
        surface: '#6200EE',
        primary: '#6200EE',
        'primary-darken-1': '#3700B3',
        secondary: '#03DAC6',
        'secondary-darken-1': '#018786',
        error: '#B00020',
        info: '#2196F3',
        success: '#4CAF50',
        warning: '#FB8C00',
    }
}

const dark = {
    dark: true,
    colors: {
    }
}

export default createVuetify({
    theme: {
        defaultTheme: 'light',
        themes: {
            light,
            dark
        }
    }
})
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422235/72f54bf7-5238-a3b7-5555-c83712259382.png)