Files
agent-skills/skills/vue-best-practices/reference/tailwind-dynamic-class-generation.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

3.1 KiB

Tailwind CSS Dynamic Class Generation

Rule

Never construct Tailwind CSS class names dynamically using string concatenation or template literals. Tailwind's build process cannot detect dynamically generated class names, causing styles to be missing in production.

Why This Matters

  • Tailwind uses static analysis at build time to determine which CSS classes to include
  • Dynamically constructed class names (e.g., bg-${color}-500) are invisible to Tailwind's scanner
  • Classes work in development with JIT but fail silently in production builds
  • This is a common source of "it works locally but not in production" bugs

Bad Code

<script setup>
const props = defineProps({
  color: String, // 'red', 'blue', 'green'
  size: String   // 'sm', 'md', 'lg'
})
</script>

<template>
  <!-- WRONG: Tailwind cannot detect these classes -->
  <div :class="`bg-${color}-500 text-${size}`">
    Content
  </div>

  <!-- WRONG: String concatenation -->
  <div :class="'p-' + padding">
    Content
  </div>

  <!-- WRONG: Template literal in array -->
  <div :class="[`gap-x-${spacing}`]">
    Content
  </div>
</template>

Good Code

<script setup>
const props = defineProps({
  color: String,
  size: String
})

// Use a mapping object with complete class names
const colorClasses = {
  red: 'bg-red-500',
  blue: 'bg-blue-500',
  green: 'bg-green-500'
}

const sizeClasses = {
  sm: 'text-sm p-2',
  md: 'text-base p-4',
  lg: 'text-lg p-6'
}
</script>

<template>
  <!-- CORRECT: Full class names that Tailwind can detect -->
  <div :class="[colorClasses[color], sizeClasses[size]]">
    Content
  </div>
</template>

Using Conditional Objects

<script setup>
const props = defineProps({
  variant: String // 'primary', 'secondary', 'danger'
})
</script>

<template>
  <!-- CORRECT: All class names are complete strings -->
  <button :class="{
    'bg-blue-500 hover:bg-blue-600': variant === 'primary',
    'bg-gray-500 hover:bg-gray-600': variant === 'secondary',
    'bg-red-500 hover:bg-red-600': variant === 'danger'
  }">
    Click me
  </button>
</template>

Safelist for Truly Dynamic Classes

If you must use dynamic classes, add them to Tailwind's safelist:

// tailwind.config.js
module.exports = {
  safelist: [
    'bg-red-500',
    'bg-blue-500',
    'bg-green-500',
    // Or use patterns (use sparingly - increases bundle size)
    {
      pattern: /bg-(red|blue|green)-(100|500|900)/
    }
  ]
}

Alternative: CSS Custom Properties

For truly dynamic values, use CSS custom properties:

<script setup>
const props = defineProps({
  customColor: String // Any hex color
})
</script>

<template>
  <!-- Use CSS variable for truly dynamic values -->
  <div
    class="dynamic-bg"
    :style="{ '--dynamic-color': customColor }"
  >
    Content
  </div>
</template>

<style>
.dynamic-bg {
  background-color: var(--dynamic-color);
}
</style>

References