Zum Inhalt springen

Style Binding

Seit Vue 3.2 können reaktive JavaScript-Werte direkt in <style>-Blöcken verwendet werden.

Die v-bind()-Funktion in CSS ermöglicht dynamische Styles ohne Inline-Styles oder CSS-Variablen manuell zu setzen.

<script setup>
import { ref } from 'vue'
const color = ref('red')
const fontSize = ref(16)
</script>
<template>
<p class="text">Dynamisch gestyled!</p>
<button @click="color = 'blue'">Blau</button>
<button @click="fontSize += 2">Größer</button>
</template>
<style scoped>
.text {
color: v-bind(color);
font-size: v-bind(fontSize + 'px');
}
</style>
<script setup>
import { ref, reactive } from 'vue'
const theme = reactive({
primary: '#3b82f6',
secondary: '#10b981',
borderRadius: 8
})
const isLarge = ref(false)
</script>
<style scoped>
.card {
/* Objekt-Properties */
background: v-bind('theme.primary');
border-color: v-bind('theme.secondary');
/* Mit Einheit */
border-radius: v-bind('theme.borderRadius + "px"');
/* Ternary */
padding: v-bind('isLarge ? "2rem" : "1rem"');
}
</style>
<script setup>
import { ref, computed } from 'vue'
const hue = ref(200)
const backgroundColor = computed(() => `hsl(${hue.value}, 80%, 50%)`)
const textColor = computed(() => `hsl(${hue.value}, 80%, 10%)`)
</script>
<template>
<div class="box">
<input type="range" v-model="hue" min="0" max="360" />
<p>Hue: {{ hue }}°</p>
</div>
</template>
<style scoped>
.box {
background-color: v-bind(backgroundColor);
color: v-bind(textColor);
padding: 2rem;
transition: all 0.3s ease;
}
</style>

Vue kompiliert v-bind() zu CSS Custom Properties (CSS-Variablen):

<!-- Was du schreibst -->
<style scoped>
.text {
color: v-bind(color);
}
</style>
<!-- Was Vue generiert -->
<p class="text" style="--abc123-color: red;">...</p>
<style>
.text {
color: var(--abc123-color);
}
</style>
<style scoped>
.box {
background: v-bind(bgColor);
/* Volle CSS-Power */
transition: background 0.3s;
}
</style>

Vorteile:

  • Saubere Trennung
  • CSS-Features nutzbar (transitions, media queries)
  • Scoped funktioniert
<script setup>
import { reactive, provide } from 'vue'
const theme = reactive({
primary: '#3b82f6',
secondary: '#10b981',
background: '#ffffff',
text: '#1f2937',
radius: '0.5rem'
})
provide('theme', theme)
</script>
<style scoped>
:global(:root) {
--color-primary: v-bind('theme.primary');
--color-secondary: v-bind('theme.secondary');
--color-background: v-bind('theme.background');
--color-text: v-bind('theme.text');
--border-radius: v-bind('theme.radius');
}
</style>
<script setup>
import { ref } from 'vue'
const progress = ref(0)
const rotation = ref(0)
setInterval(() => {
progress.value = (progress.value + 1) % 100
rotation.value = (rotation.value + 3) % 360
}, 50)
</script>
<template>
<div class="spinner"></div>
<div class="progress-bar"></div>
</template>
<style scoped>
.spinner {
transform: rotate(v-bind(rotation + 'deg'));
}
.progress-bar {
width: v-bind(progress + '%');
}
</style>
<script setup>
defineProps({
size: {
type: String,
default: '100px'
},
color: {
type: String,
default: 'currentColor'
}
})
</script>
<template>
<div class="icon">
<slot />
</div>
</template>
<style scoped>
.icon {
width: v-bind(size);
height: v-bind(size);
color: v-bind(color);
}
</style>
  • ref() und reactive()
  • computed()
  • Props
  • JavaScript-Expressions
  • Template Literals
  • Scoped und unscoped
  • Quotes bei Punktnotation: v-bind('obj.prop')
  • Performance bei vielen Bindings
  • SSR: Werte werden inline
  • Keine komplexen Funktionsaufrufe
<script>
export default {
data() {
return {
color: 'red',
size: 16
}
}
}
</script>
<style scoped>
.text {
/* Zugriff auf data Properties */
color: v-bind(color);
font-size: v-bind(size + 'px');
}
</style>

Offizielle Vue Dokumentation:

Release Notes: