Drie opties voor CSS

In een Vue-app heb je drie manieren om dingen te stijlen:

  1. Scoped CSS in een component<style scoped> in een .vue bestand
  2. Class binding → dynamisch een class aan/uit zetten op basis van state
  3. Globale CSS → één CSS-bestand voor je hele app (reset, basis, kleuren-variabelen)

Voor wie Tailwind wil: dat is een aparte aanpak met utility-classes. Zie de Tailwind pagina.

<style scoped> — de Vue manier

Het derde blok in een .vue bestand is voor CSS. Met scoped gelden de stijlen alleen voor dit component.

<!-- src/components/Card.vue -->
<template>
  <div class="card">
    <h2 class="title">Mijn titel</h2>
    <p>Inhoud</p>
  </div>
</template>

<style scoped>
.card {
  padding: 16px;
  border: 1px solid #ccc;
  border-radius: 8px;
}

.title {
  color: #42b883;
  margin-bottom: 8px;
}
</style>

Wat is "scoped"? Vue voegt automatisch een uniek data-attribute toe aan elke tag in dit component. De CSS-selectors gelden alleen voor elementen met dat attribute. Een .title in dit component botst niet met een .title in een ander component.

Met of zonder scoped?

  • Met scoped → bijna altijd. Voorkomt CSS-conflicten.
  • Zonder scoped → in App.vue voor globale stijlen, of als je expliciet child-components wilt stijlen.

Dynamische class binding

De grote kracht van Vue: classes op basis van state. Drie manieren:

1. Object — voorwaardelijk een class

<script setup>
import { ref } from 'vue'

const isActive = ref(true)
const hasError = ref(false)
</script>

<template>
  <div :class="{ active: isActive, error: hasError }">
    Hallo
  </div>
</template>

Resultaat (omdat isActive true, hasError false):

<div class="active">Hallo</div>

2. Array — meerdere classes combineren

<script setup>
const baseClass = 'btn'
const variantClass = 'btn-primary'
</script>

<template>
  <button :class="[baseClass, variantClass]">Klik</button>
</template>

3. Statisch + dynamisch mixen

<script setup>
import { ref } from 'vue'

const isLoading = ref(true)
</script>

<template>
  <button class="btn" :class="{ loading: isLoading }">
    Verzenden
  </button>
</template>

Resultaat als isLoading true: <button class="btn loading">. De statische class="btn" en de dynamische :class="{...}" worden samengevoegd.

Vue vs React: In React schrijf je className={isActive ? 'card active' : 'card'}. In Vue is dat :class="['card', { active: isActive }]" — duidelijker en korter.

Inline style binding

Voor stijlen die je niet vooraf weet (kleuren uit een database, dynamische dimensies):

<script setup>
import { ref } from 'vue'

const color = ref('red')
const fontSize = ref(20)
</script>

<template>
  <p :style="{ color: color, fontSize: fontSize + 'px' }">
    Gekleurde tekst
  </p>
</template>

Gebruik dit spaarzaam. Inline styles zijn moeilijker te overschrijven en maken je templates rommelig. Voor "deze tekst is rood" of "verberg dit element": gebruik een class binding. Inline style is alleen écht nodig bij truly dynamische waardes.

Let op CSS-property namen

In JavaScript schrijf je fontSize (camelCase), niet font-size (kebab-case):

:style="{ fontSize: '20px' }"     // ✅
:style="{ 'font-size': '20px' }"   // ✅ ook (met quotes)
:style="{ font-size: '20px' }"     // ❌ syntax error

Globale CSS

Voor stijlen die overal moeten gelden (reset, fonts, kleur-variabelen): gebruik een CSS-bestand dat je in main.js importeert.

1. Maak src/assets/main.css

/* Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* CSS variabelen */
:root {
  --primary: #42b883;
  --text: #333;
  --bg: #fff;
}

body {
  font-family: system-ui, sans-serif;
  color: var(--text);
  background: var(--bg);
}

2. Importeer in main.js

import { createApp } from 'vue'
import App from './App.vue'
import './assets/main.css'   // ← globale CSS

createApp(App).mount('#app')

3. Gebruik de variabelen in elk component

<style scoped>
.button {
  background: var(--primary);
  color: white;
}
</style>

Aanpak die altijd werkt: globale CSS voor resets en variabelen, scoped CSS voor component-specifieke stijlen. Geen conflicten, makkelijk te onderhouden.

Belangrijke Regels

  • Default: <style scoped> per component
  • Dynamische classes:class="{ active: isActive }"
  • Globale CSS → één bestand in src/assets/, geïmporteerd in main.js
  • CSS-variabelen op :root voor app-brede kleuren/spacing
  • Inline style → alleen voor écht dynamische waardes

Veelgemaakte Fouten

Fout — :class als statische string:

<div :class="card active">...</div>
<!-- ❌ "card" en "active" worden als JS-variabelen behandeld -->

Goed:

<div class="card active">...</div>          <!-- statisch -->
<div :class="['card', 'active']">...</div>  <!-- dynamisch (array) -->

Fout — CSS-properties in kebab-case zonder quotes:

<div :style="{ background-color: 'red' }">
<!-- ❌ syntax error -->

Goed:

<div :style="{ backgroundColor: 'red' }">          <!-- ✅ -->
<div :style="{ 'background-color': 'red' }">      <!-- ✅ ook -->

Fout — geen scoped → CSS lekt:

<!-- Card.vue -->
<style>
.title { color: red; }   /* ❌ raakt ALLE .title in je hele app */
</style>

Goed:

<style scoped>
.title { color: red; }   /* ✅ alleen voor dit component */
</style>