LoginSignup
31
19

More than 3 years have passed since last update.

【Vuetify】テーマ設定に応じてスタイルを変える

Last updated at Posted at 2019-04-22

Vuetifyを使ってViewを組み立てていくと、自前のコンポーネントでもテーマを設定できるようにしたくなります。
そんなときのTipsです。

Vuetifyにおけるテーマとは

Vuetifyではライト・ダークのテーマ設定をすることができます。
テーマに応じたスタイルが全体に適用されるので、フラグ一つでアプリケーション全体のテーマを切り替えることができます。
Application theming — Vuetify.js

また、Appレベルだけでなく、各コンポーネントでも個別のテーマを設定することができます。
例えば、全体ではライトテーマだけど、一部のカードだけはダークテーマにしたいということができます。

v-app(light)
  ...
    v-card(dark)
      v-card-text 
        // ここはダーク

テーマを設定できるコンポーネントは、自分自身に設定されていればそれを利用しますが、何も設定されていない場合は近い親のテーマが引き継がれます。

v-card(light)
  // ここはライト
  v-card(dark)
    // ここはダーク
    v-card
      // ここは近い親のを引き継ぐのでダーク

この中で、自作のコンポーネントもテーマに応じてスタイルを変えるのはどのようにすればよいのでしょうか。:thinking:

Vuetifyのソースを眺めてみる

まずどんな風に実現しているのかVuetifyのソースを見てみましょう。
テーマの処理は、Themeableというミックスインが司どっています。
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/mixins/themeable/index.ts

Themeableではこんなことをしてくれます。

テーマの受け取り

propsでテーマを受け取ることができます。

  props: {
    dark: {
      type: Boolean,
      default: null
    } as PropValidator<boolean | null>,
    light: {
      type: Boolean,
      default: null
    } as PropValidator<boolean | null>
  },

祖先のどこからかprovideされるテーマの受け取り

(いきなりちょっと難しいですが。。。)
祖先のどこからか提供されるテーマをinject機能を使って受け取ります。
どこからも提供されない場合はデフォルトでライトテーマです。

  inject: {
    theme: {
      default: {
        isDark: false
      }
    }
  },

現在のテーマの判定

computedプロパティで現在のテーマを判定します。
propsで渡されるものが最優先で、指定がなければ祖先からprovideされた値を利用します。

  computed: {
    isDark (): boolean {
      if (this.dark === true) {
        // explicitly dark
        return true
      } else if (this.light === true) {
        // explicitly light
        return false
      } else {
        // inherit from parent, or default false if there is none
        return this.theme.isDark
      }
    },
    // ...

判定したテーマを子孫に提供

上記のisDarkwatchしていて、ミックスインの内部に持っているthemeableProvideというオブジェクトにセットします。

  watch: {
    isDark: {
      handler (newVal, oldVal) {
        if (newVal !== oldVal) {
          this.themeableProvide.isDark = this.isDark
        }
      },
      immediate: true
    }
  }

そのthemeableProvideを子孫に向けてprovideします。

 provide (): object {
    return {
      theme: this.themeableProvide
    }
  },

provide/injectって?

いきなり出てきましたが、provideinjectは、Vue.jsの2.2から追加された機能です。

この 1 組のオプションは、コンポーネントの階層がどれほど深いかにかかわらず、それらが同じ親チェーン内にある限り、祖先コンポーネントが、自身の子孫コンポーネント全てに対する依存オブジェクトの注入役を務めることができるようにするために利用されます。React に精通している人は、 React のコンテキストの特徴と非常によく似ていると捉えると良いでしょう。

この機能を使って各コンポーネントは親のテーマを受け取って、自身のテーマを決定しているのですね。

自作コンポーネントで同じようなことするには?

自作のコンポーネントでも同じようなことをしたかったら、Vuetifyのコンポーネントから渡されるテーマを受け取ってしまえばよさそうですね。
つまり、VuetifyのThemeableを利用するか、またはThemeable相当のものを自作して、provideされているものを受け取ってやりましょう。

ちなみにTypeScriptでvue-mixin-decoratorvue-property-decoratorを使う場合はこんな感じで書けます。
(provideはさぼってます :joy:)

import Vue from 'vue';
import { Mixin } from 'vue-mixin-decorator';
import { Prop, Inject } from 'vue-property-decorator';

interface Theme {
    isDark: boolean;
}

@Mixin
export default class Themeable extends Vue {
    @Inject({ from: 'theme', default: { isDark: false } }) theme: Theme;
    @Prop() dark: string;
    @Prop() light: string;

    get isDark() {
        if (this.dark) {
            return true;
        }
        if (this.light) {
            return false;
        }
        return this.theme.isDark;
    }

    get themeClasses() {
        return {
            'theme--dark': this.isDark,
            'theme--light': !this.isDark,
        };
    }
}

使うときはこちら。

<script lang="ts">
import Vue from 'vue';
import Component from 'vue-class-component';
import { Prop } from 'vue-property-decorator';
import { Mixins } from 'vue-mixin-decorator';

import Themeable from '../../mixins/Themeable';

@Component
export default class extends Mixins<Themeable>(Themeable) {
  // 略
}
</script>

<template lang="pug">
div.hogehoge(:class="themeClasses")
  // 略
</template>

<style lang="stylus" scoped>
.hogehoge
  // ライトテーマのスタイル
  color: #000

  &.theme--dark
    // ダークテーマのスタイル
    color: #fff;
</style>

それでは良きVuetifyライフを!!!

ps.
Vuetifyのコントリビュート活動はじめました :sunglasses:

image.png

31
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
19