Vuetify CSS missing when i build for production

Viewed 2502

We purchased a web app written in Vue from someone and we developing to change/improve it. One thing we added was Vuetify so we can use the Vuetify elements and everything has been working great while in development mode, but when we build for production the CSS for Vuetify elements is missing.

I have searched for this online already and have already tried what everybody is suggesting without any luck.

Anybody has an idea of what could be wrong and why npm run build would be missing some of the CSS?

What's weird is that all the UI functionality for Vue elements is working perfectly, just the CSS is missing.

Please see code samples below.

main.js:

import '@fortawesome/fontawesome-free/css/all.css'
import Vue from "vue";
import App from "./App.vue";
import VueMoment from "vue-moment";
import VueAnalytics from "vue-analytics";
import VueMeta from "vue-meta";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
  faCoffee,
  faPlusCircle,
  faChartLine,
  faChevronDown,
  faMobile,
  faEnvelope,
  faClock,
  faUsers,
  faPaperPlane,
  faCheckCircle,
  faCheck,
  faLeaf,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import axios from "axios";
import router from "./router";
import store from "./store";
import vuetify from './plugins/vuetify';
import Vuetify from 'vuetify/lib'

library.add([
  faCoffee,
  faPlusCircle,
  faChartLine,
  faChevronDown,
  faMobile,
  faEnvelope,
  faClock,
  faUsers,
  faPaperPlane,
  faCheckCircle,
  faCheck,
  faLeaf,
]);

Vue.use(VueAnalytics, {
  id: "xxx",
  router,
});
Vue.use(VueMoment);
Vue.use(VueMeta);
Vue.component("font-awesome-icon", FontAwesomeIcon);

Vue.use(Vuetify)

axios.interceptors.response.use(undefined, async function (error) {
  if (error.response.status === 401) {
    await store.dispatch("auth/logout");
    router.push("/login");
  }
  return Promise.reject(error);
});

// Plugins
// ...

// Sass file
require("./assets/styles/main.css");

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  vuetify,
  render: (h) => h(App)
}).$mount("#app");

App.vue:

<template>
  <v-app>

    <v-main>
      <router-view/>
    </v-main>
  </v-app>
</template>

<style>
  .text-white {
      color: #fff !important;
  }

  .text-gray-600 {
    color: #757575 !important;
  }

  .font-semibold, .text-gray-700 {
    color: #616161 !important;
  }
</style>

package.json:

{
  "name": "reviewgrower-spa",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "deploy": "git push dokku master"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^1.2.25",
    "@fortawesome/free-solid-svg-icons": "^5.11.2",
    "@fortawesome/vue-fontawesome": "^0.1.8",
    "@fullhuman/postcss-purgecss": "^1.3.0",
    "axios": "^0.19.0",
    "chart.js": "^2.9.4",
    "core-js": "^2.6.10",
    "i": "^0.3.6",
    "jquery": "^3.5.1",
    "npm": "^6.13.0",
    "tailwindcss-spinner": "^0.2.0",
    "tailwindcss-toggle": "github:TowelSoftware/tailwindcss-toggle",
    "url-parse": "^1.4.7",
    "vue": "^2.6.10",
    "vue-analytics": "^5.17.2",
    "vue-chartjs": "^3.5.1",
    "vue-click-outside": "^1.0.7",
    "vue-clickaway": "^2.2.2",
    "vue-feather-icons": "^4.22.0",
    "vue-js-toggle-button": "^1.3.3",
    "vue-meta": "^1.6.0",
    "vue-moment": "^4.0.0",
    "vue-router": "^3.1.3",
    "vue-stripe-elements-plus": "^0.2.10",
    "vuetify": "^2.4.0",
    "vuex": "^3.0.1",
    "vuex-persist": "^2.1.1"
  },
  "devDependencies": {
    "@fortawesome/fontawesome-free": "^5.15.2",
    "@vue/cli-plugin-babel": "^3.12.1",
    "@vue/cli-plugin-eslint": "^3.12.1",
    "@vue/cli-service": "^3.12.1",
    "babel-eslint": "^10.0.3",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.2.3",
    "sass": "^1.32.0",
    "sass-loader": "^7.1.0",
    "tailwindcss": "^1.1.3",
    "vue-cli-plugin-vuetify": "~2.1.0",
    "vue-template-compiler": "^2.5.21",
    "vuetify-loader": "^1.7.0"
  }
}
3 Answers

It's a little tough to understand what is missing where. If you think that is just missing then please try adding css onto the HTML file from the cdn and check the working.

<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">

I see that you are using webpack to compile the code. So, this could be also something related to webpack configuration. In your webpack rules do you have rules for css and scss. Because vuetify files are in scss.

My webpack configuration is as below when I do these type of circus.

--webpack.config.js--
const path = require("path");

const VuetifyLoaderPlugin = require("vuetify-loader/lib/plugin");
const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
watch: true,
entry: {
 main: 'main.js'
},
module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.vue$/,
        use: "vue-loader",
      },
      {
        test: /\.s(c|a)ss$/,
        use: [
          "vue-style-loader",
          "css-loader",
          {
            loader: "sass-loader",
            // Requires sass-loader@^8.0.0
            // options: {
            //   implementation: require('sass'),
            //   sassOptions: {
            //     fiber: require('fibers'),
            //     indentedSyntax: true // optional
            //   },
            // },
          },
        ],
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(),
    new VuetifyLoaderPlugin({
      /**
       * This function will be called for every tag used in each vue component
       * It should return an array, the first element will be inserted into the
       * components array, the second should be a corresponding import
       *
       * originalTag - the tag as it was originally used in the template
       * kebabTag    - the tag normalised to kebab-case
       * camelTag    - the tag normalised to PascalCase
       * path        - a relative path to the current .vue file
       * component   - a parsed representation of the current component
       */
      match(originalTag, { kebabTag, camelTag, path, component }) {
        if (kebabTag.startsWith("core-")) {
          return [
            camelTag,
            `import ${camelTag} from '@/components/core/${camelTag.substring(
              4
            )}.vue'`,
          ];
        }
      },
    }),
  ],
}

Check your postcss.config.js, see if it has something to do with the purgecss. You have to config the whitelist to ignore the vuetify styles. Here is a sample for your reference:

const autoprefixer = require("autoprefixer");
const postcssImport = require("postcss-import");
const purgecss = require("@fullhuman/postcss-purgecss");
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
let plugins = [];
if (IS_PROD) {
  plugins.push(postcssImport);
  plugins.push(
    purgecss({
      content: [
        "./src/**/*.vue",
        "./public/**/*.html",
        `./node_modules/vuetify/src/**/*.ts`,
        `./node_modules/vuetify/dist/vuetify.css`
      ],
      defaultExtractor (content) {
        const contentWithoutStyleBlocks = content.replace(/<style[^]+?<\/style>/gi, '')
        return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
      },
        safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ],

        whitelist: [
            'container',
            'row',
            'spacer',
            'aos-animate',
            'col',
            '[type=button]',
            'v-application p',
          ],
          whitelistPatterns: [
            /^v-.*/,
            /^col-.*/,
            /^theme-.*/,
            /^rounded-.*/,
            /^data-aos-.*/,
            /^(red|grey)--text$/,
            /^text--darken-[1-4]$/,
            /^text--lighten-[1-4]$/
          ],
          whitelistPatternsChildren: [
            /^post-content/,
            /^v-input/,
            /^swiper-.*/,
            /^pswp.*/,
            /^v-text-field.*/,
            /^v-progress-linear/
          ]
    })
  );
}

module.exports = {
    plugins:[
        require('cssnano')({
            preset: 'default'
        }),
        require('postcss-pxtorem')({
            remUnit:15, //每个rem对应的px值
            threeVersion:true
        }),
        ...plugins,autoprefixer
    ]
}``

You are simply missing an include in your main.js (see vuetify docs):

import 'vuetify/dist/vuetify.min.css'

This will ensure that webpack includes the vuetify styles in the bundled CSS for production. This fixed the same issue for me (i.e. it worked locally but not in production).

Related