🌱

【Vue3】CSS クラスの動的切り替えで、CSS Modules で定義した識別子を参照する

2023/06/13に公開

例えば、props の値を元に割り当てる class を動的に切り替えたい場合、以下のような実装をしてもうまくスタイルが当たってくれない。

<script setup lang="ts">
export type ButtonProps = {
  type: 'primary' | 'secondary' | 'info';
  text: string;
};
defineProps<ButtonProps>();
</script>

<template>
  <button type="button" :class="[$style.button, `${$style}.${type}`]">
    {{ text }}
  </button>
</template>

<style module>
.button {
  padding: 10px;
}

.primary {
  color: red;
}

.secondary {
  color: yellow;
}

.info {
  color: blue;
}
</style>

動的に割り当てたいクラスが 2 つ程度であれば、以下のように三項演算子を使えばいいけど、 3 つい上になってくると三項演算子では対応できない。

<template>
  <button
    type="button"
    :class="[$style.button, props.type === 'primary' ? $style.primary : $style.secondary]"
  >
    {{ text }}
  </button>
</template>

解決策1. setup 関数内で CSS Modules で定義した識別子を参照する

useCssModule を使う方法。
https://ja.vuejs.org/api/sfc-css-features.html#usage-with-composition-api

<script setup lang="ts">
import { useCssModule } from 'vue';

export type ButtonProps = {
  type: 'primary' | 'secondary' | 'info';
  text: string;
};
defineProps<ButtonProps>();

const $style = useCssModule();
const classArray = [
  $style.button,
  props.type === 'primary' && $style.primary,
  props.type === 'secondary' && $style.secondary,
  props.type === 'info' && $style.info,
];
</script>

<template>
  <button type="button" :class="classArray">
    {{ text }}
  </button>
</template>

<style module>
.button {
  padding: 10px;
}

...省略
</style>

参照を追うのが若干大変な気もする?

解決策2. :class ディレクティブ内で式展開を行う

<script setup lang="ts">
export type ButtonProps = {
  type: 'primary' | 'secondary' | 'info';
  text: string;
};
defineProps<ButtonProps>();
</script>

<template>
  <button
    type="button"
    :class="[
      $style.button,
      props.type === 'primary' && $style.primary,
      props.type === 'secondary' && $style.secondary,
      props.type === 'info' && $style.info,
    ]"
  >
    {{ text }}
  </button>
</template>

<style module>
.button {
  padding: 10px;
}

...省略
</style>

:class ディレクティブ内が若干賑やかになるけど、個人的にはこっちの方が素直で好きかも。

解決策3. ドット記法ではなくブラケット記法で参照する(New!)

ブラケット記法を使うと簡単にできるらしい。

<script setup lang="ts">
export type ButtonProps = {
  type: 'primary' | 'secondary' | 'info';
  text: string;
};
defineProps<ButtonProps>();
</script>

<template>
  <button type="button" :class="[$style.button, $style[type]]">
    {{ text }}
  </button>
</template>

<style module>
.button {
  padding: 10px;
}

.primary {
  color: red;
}

.secondary {
  color: yellow;
}

.info {
  color: blue;
}
</style>

$style も JS のオブジェクトなんだから、そりゃそうだよねっていう。

参考

https://stackoverflow.com/questions/71164906/how-to-use-css-module-in-setup-function

Discussion