using dynamic CSS classes in a Vue app

I am currently in the process of developing a component with vuejs and tailwindcss. I have run into an issue where the colors for the background and text are not being applied properly.

<template>
  <div :class="[isDark ? `bg-${color}-900 text-white` : `bg-${color}-200 text-${color}-900`, 'p-4 rounded-md']">
    <slot />
  </div>
</template>

<script>
export default {
  name: "TCard",
  props: {
    color: {
      type: String,
      default: "gray",
      validator: function (value) {
        return ["gray", "red", "yellow", "green", "blue", "indigo", "purple", "pink"].includes(value);
      },
    },
    isDark: {
      type: Boolean,
      default: false
    }
  },
};
</script>

Answer №1

According to the specified documentation:

The key aspect of how Tailwind extracts class names is that it will exclusively identify classes that exist as complete unbroken strings in your source files.

If you try to interpolate strings or concatenate partial class names together, Tailwind won't be able to detect them and hence won't generate the corresponding CSS:

Avoid forming class names dynamically

<div class="text-{{ error ? 'red' : 'green' }}-600"></div>

In the given example, the strings text-red-600 and text-green-600 are not present, so Tailwind will skip generating those classes. Remember to always use complete class names:

Utilize full class names at all times

<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

You may want to employ a map of classes instead like:

const MAP = {
  gray: {
    light: 'bg-gray-200 text-gray-900',
    dark: 'bg-gray-900 text-white',
  },
  red: {
    light: 'bg-red-200 text-red-900',
    dark: 'bg-red-900 text-white',
  },
  // …
};

const TCard = {
  props: {
    color: {
      type: String,
      default: "gray",
      validator: function (value) {
        return value in MAP;
      },
    },
    isDark: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    colors() {
      return MAP[this.color][this.isDark ? 'dark' : 'light'];
    },
  },
  template: `
<div :class="[colors, 'p-4 rounded-md']">
  <slot />
</div>`
};

Vue.createApp({ components: { TCard }}).mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.3.4/vue.global.min.js" integrity="sha512-Wbf9QOX8TxnLykSrNGmAc5mDntbpyXjOw9zgnKql3DgQ7Iyr5TCSPWpvpwDuo+jikYoSNMD9tRRH854VfPpL9A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com"></script>

<div id="app">
  <t-card color="red">Foo</t-card>
  <t-card color="gray" is-dark>Bar</t-card>
</div>

This approach maintains the coherence between the classes used and the template markup. The validator can also cross-check with the MAP to confirm the availability of a desired color.

Alternatively, you could opt for using safelist within your Tailwind configuration such as:


module.exports = {
  …
  safelist: [
    { pattern: /^(bg|text)-(gray|red|yellow|green|blue|indigo|purple|pink)-900$/ },
    { pattern: /^bg-(gray|red|yellow|green|blue|indigo|purple|pink)-200$/ },
  ],
  …
};

The drawback here is that this would remain detached from your templates, potentially leading to a disconnect. This divergence might make it challenging to synchronize updates and switching the context for any edits.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Custom .woff Format Fonts in Sendgrid: A Guide

Having trouble with implementing custom fonts in Sendgrid. While Google fonts work fine, the custom .woff format font doesn't seem to cooperate. I've attempted three solutions below. Solution number 1 shows up in the Preview tab but not in the em ...

The arrangement of CSS code is crucial for it to work properly; any deviation from the correct order

I am facing a problem where I am trying to display a circular div using CSS, but it only works if the background image property is set first. Typically, this wouldn't be an issue if the image wasn't being dynamically inserted using PHP code. I ...

Generate a unique border using jQuery randomization techniques

I've been experimenting with creating a random number and having the border-radius of my div change on every pageload: <script> $(".card-body").each(function(){ var rand = Math.floor(Math.random() * 100) + 1; $(this).css( { borderRadius: rand ...

Having difficulties with hovering over a td that has a rowspan

I am encountering some challenges when attempting to hover over a td with rowspan in my HTML table. Despite searching for a solution, I have not been successful. I came across suggestions on forums that hinted at using JS/jQuery. Could someone please provi ...

Is it possible to generate a table without any grid lines present?

Currently, I am in the process of designing a table that will function as a popup when a link is clicked within my imported SQL table. An example of this table is included with this message for reference. After conducting thorough research, I attempted to ...

Tips for personalizing error messages for the "required" field by utilizing a dictionary feature on VeeValidate in Vue.Js

I am trying to update the error message that appears when an input field with the "cpf" rule is left empty (meaning it does not meet the "required" rule). I believe using the "dictionary method" with custom messages is the solution, but I am struggling to ...

Making a Dialog resizable with jQuery's Resizable Helper

Is there a way to implement the Helper feature from jQuery Resizable, which only shows a frame while resizing the container, in a Dialog? ...

Header with absolute positioning doesn't play nice when nudged in Chrome

Take a look at the HTML page on this JSFiddle link: http://jsfiddle.net/rb8rs70w/2/ The page features a "sticky" header created using CSS with the property position: absolute. ... <body class="body-menu-push"> <header role="banner"> ...

Calculating values within dynamically generated elements requires utilizing JavaScript to target and extract the

I am working on creating input fields inside an HTML table using Vue.js. On click of a button, I want to perform some calculations based on the input values. However, it seems that the calculations are not happening as desired. What I have attempted so fa ...

What is the best way to limit the top margin for a fixed element?

Recently, I came across this code snippet: jQuery(document).ready(function($){ $( window ).scroll(function() { var current_top = $(document).scrollTop(); var heights = window.innerHeight; document.getElementById("sHome").styl ...

Height of list items in image gallery does not automatically readjust

I'm facing an issue regarding the height of an li element. Check out this CodePen link for reference. <div class="row fp-gallery"> <ul class="large-block-grid-4 medium-block-grid-4 small-block-grid-2"> <li> <a href= ...

iOS devices experiencing issues with fixed positioning

Currently, I am in the process of implementing a few instances of , but I am encountering some issues with the CSS. When viewing the website on an iPad or iPhone, the calendar is positioned correctly as the container covers the window like a fixed position ...

The navigation bar struggles to display list elements without proper line breaks

Recently, I've been immersed in a web development project for a company. For the navigation bar, I opted to use the FlexNav JQuery plugin. While referencing the example on , I encountered an issue when attempting to add more than 5 list elements; they ...

Using the scroll feature within the tooltip component in React with @material-ui/core/Tooltip can cause the entire page to

I am currently working with a tooltip created using the @material-ui/core/Tooltip library, and it is functioning correctly. You can see an example of the tooltip in action at this link: https://codesandbox.io/s/kgccc. https://i.stack.imgur.com/fMgGb.png ...

Utilize Element-UI to effectively close dropdown menus

In my Vue application with ElementUI, I have a drop down menu that needs to be explicitly closed after some logic is applied. The process is as follows: User loads the page, selects a name from the drop-down - the name value is saved in the database User ...

Positioning a div element within a list item using CSS

Currently, I am in the process of constructing a megamenu. In this setup, there is an unordered list that is positioned relative to its parent container. Within this list, there are individual items that contain links along with separate div containers th ...

Troubleshooting a Vue 2 component's prop receiving an incorrect value

I'm currently working on creating a menu that navigates between categories. When a category has a sub-category, it should return a boolean value indicating whether it has a sub-category or not. <template> <select><slot></slot ...

What is the reason for `this` becoming undefined within the filter() function in VueJS?

Currently, I am in the process of designing a Date of Birth (DOB) Form. The interesting part is that I have incorporated VueJS into the form to enhance user experience. The unique feature of this form is that users are required to input their month of bir ...

Improving code quality and consistency in Javascript: Tips for writing better code

Hey, I've been experimenting with some code using ajax and have ended up with a lot of repetitive lines. Is there a way to streamline the code without losing functionality? I keep using the .done method multiple times when I could probably just use it ...

Adjust the initial scroll position to - apply overflow-x: scroll - on the specified element

I have an image that can be scrolled to the right on screens that do not fit the full width, but I want the center of the image to be displayed first instead of the left side. Below is my React code: import React, { useEffect, useRef, useState } from &qu ...