🌾

【Vue3.2】styleタグ内でJavaScript変数をバインドできる

2021/09/20に公開

Vue.js3.2からは、JavaScriptの変数をCSS変数としてバインドできるようになりました。

つまりは、CSSの値を動的に設定できるということです。

ものは試しのやってみましょう。

<script setup lang="ts">
import { ref } from "@vue/reactivity";
const color = ref("#000000");
</script>

<template>
  <h1 class="title">タイトル</h1>
  <input type="color" v-model="color" />
</template>

<style>
.title {
  color: v-bind(color);
}
</style>

<script>タグ内で宣言したcolorという変数を<style>タグ内でv-bind(color);として使用しています。

生成されたCSSを見てみると、以下のような感じになっています。

スクリーンショット 2021-09-19 22.41.48

  1. --7ba5bd90-colorのようにハッシュ化されたCSS変数がインラインスタイルとして定義されます。
  2. <style>タグでバインドしたJavaScippt変数は上記のハッシュ化されたCSS変数が適用されます。

もちろん、JavaScriptで宣言したリアクティブな変数の値が変更されるたびにCSS変数の値も変化されます。

css-bind

ここまでが基本的な機能の説明です。他にもいろいろ試してみましょう。

JavaScript式の展開

VSCodeにはなにか怒られるけれど、<template>でのv-bindと同じく三項演算子等JavaScriptの式を評価させることができます。

JavaSciptの式を評価させる場合にはクォートで加工必要があるようです。

<script setup lang="ts">
import { ref } from "@vue/reactivity";

const theme = ref<"dark" | "light">("dark");

const toggleTheme = () => {
  if (theme.value === "dark") {
    theme.value = "light";
  } else {
    theme.value = "dark";
  }
};
</script>

<template>
  <div class="container">
    <h1 class="title">タイトル</h1>
    <button @click="toggleTheme">テーマ変更</button>
  </div>
</template>

<style scoped>
.container {
  height: 100vh;
  width: 100vw;
  background-color: v-bind('theme === "dark" ? "#000": "#fff"');
  color: v-bind('theme === "dark" ? "#fff": "#000"');
}
</style>

css-bind

配列やオブジェクトを渡す

v-bindに配列を渡すとカンマ区切りで展開されます。単純にarray#toStringが呼ばれているようです。

.title {
  color: v-bind([ "red", "green", "blue" ]);
}
style attribute {
    --7ba5bd90-colors: red,green,blue;
}

オブジェクトも同様です。

<script setup lang="ts">
import { ref } from "@vue/reactivity";

const themes = ref({
  light: {
    textColor: "#fff",
  },
  dark: {
    textColor: "#000",
  },
});
</script>

<style scoped>
.title {
  color: v-bind(themes);
}
</style>
style attribute {
    --7ba5bd90-themes: [object Object];
}

v-bindが使える場所

<style>タグ内ならどこでもv-bindが使えるわけではないっぽい。

<style scoped>
// Unknown word
.container {
  v-bind(container)
}
</style>
<style scoped>
// Unknown word
v-bind(container)
</style>

関数呼び出し

JavaScriptの式を評価できるので、こちらも当然できますね。

<script setup lang="ts">
const textColor = () => {
  return Math.random() < 0.5 ? "red" : "blue";
};
</script>

<template>
  <div class="container">
    <h1 class="title">タイトル</h1>
  </div>
</template>

<style scoped>
.title {
  color: v-bind("textColor()");
}
</style>

関数宣言でも有効です

<script setup lang="ts">
function textColor() {
  return Math.random() < 0.5 ? "red" : "blue";
}
</script>

<template>
  <div class="container">
    <h1 class="title">タイトル</h1>
  </div>
</template>

<style scoped>
.title {
  color: v-bind("textColor()");
}
</style>

CSSのコメントで囲った文字列をバインドする

CSSのコメントで囲った箇所はしっかり消えていますね。

<script setup lang="ts">
const color = `/* red */blue`;
</script>

<template>
  <div class="container">
    <h1 class="title">タイトル</h1>
  </div>
</template>

<style scoped>
.title {
  color: v-bind(color);
}
</style>
element.style {
    --7ba5bd90-color: blue;
}

v-bindでCSS変数の参照

これはできないです。

<script setup lang="ts">
const color = "--main-bg-color";
</script>

<template>
  <div>
    <h1 class="color">タイトル</h1>
    <button class="color" @click="onclick">Button</button>
  </div>
</template>

<style>
:root {
  --main-bg-color: brown;
}
</style>

<style scoped>
.color {
  color: var(v-bind(color));
}
</style>

これはできます。

<script setup lang="ts">
const color = "var(--main-bg-color)";
</script>

<template>
  <div>
    <h1 class="color">タイトル</h1>
    <button class="color" @click="onclick">Button</button>
  </div>
</template>

<style>
:root {
  --main-bg-color: brown;
}
</style>

<style scoped>
.color {
  color: v-bind(color);
}
</style>

バインドしたCSSクラスを使用している要素のstyleを動的nullに変更すると・・・?

つまりこういうこと

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

const style = ref<any>({ fontSize: "40px" });
const onclick = () => {
  style.value = null;
};

const color = "red";
</script>

<template>
  <h1 class="color" :style="style">タイトル</h1>
  <button class="color" @click="onclick">Button</button>
</template>

<style scoped>
.color {
  color: v-bind(color);
}
</style>

正しい動作なのかよくわからないですが、styleをバインドしていた場合その値をnullにするとインラインスタイルに展開されていたCSS変数も消えてしましまいます。

style-binding

ちなみに、<template>にルート要素がある場合その要素に対してCSS変数が展開されるので、ルート要素のstyleをnullにするとすべてのCSS変数が消えてしまいます。

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

const style = ref<any>({ fontSize: "20px" });
const onclick = () => {
  style.value = null;
};

const color = "red";
</script>

<template>
  <div :style="style">
    <h1 class="color">タイトル</h1>
    <button class="color" @click="onclick">Button</button>
  </div>
</template>

<style scoped>
.color {
  color: v-bind(color);
}
</style>

root-style-binding

GitHubで編集を提案

Discussion