🌱
【Vue3】CSS クラスの動的切り替えで、CSS Modules で定義した識別子を参照する
例えば、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 を使う方法。
<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>
参照を追うのが若干大変な気もする?
:class
ディレクティブ内で式展開を行う
解決策2. <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 のオブジェクトなんだから、そりゃそうだよねっていう。
参考
Discussion