vuetifyjs / vuetify

🐉 Vue Component Framework
https://vuetifyjs.com
MIT License
39.12k stars 6.92k forks source link

[Feature Request] Scopes CSS classes at v-application to do not conflict with bootstrap and others view frameworks #8530

Closed kayran closed 3 years ago

kayran commented 4 years ago

Problem to solve

Today we have a large system made with bootstrap and other libraries. This system is not made with Vue, but recently we adopted Vue and see in Vuetify a useful lib to make solutions for our software with Vue fastly.

Due to the size of our system, it's impossible to make all the system again with Vue, and we need to make little parts of our system in Vue, communicating with the old one sometimes.

We need to get the CDN version of vuetify with the style rules scoped with v-application, like container, row, col-*, etc.

With the CSS scoped with the v-application class, we can use it in some parts of our system without problems of Vuetify rules overwriting the Bootstrap ones

Proposed solution

All classes of Vuetify made scoped in Vuetify css.

".v-application .container" instead only ".container" ".v-application .row" instead only ".row" ...

Aaronm14 commented 4 years ago

I'm actually running into nearly the same problem, although I have the ability to use webpack (the laravel-mix library, unfortunately). I think there is probably a way to do it properly, but have not had luck. In the next day or so I have to determine if I'm going to do a hacky fix and just override Vuetify's overrides, or go in another direction.

In my case, I am just trying to use the TreeView component in my case, without wanting all of the generic classes for things like .row or .container. It is odd because in the source, those classes belong to the component itself (https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VGrid/_grid.sass) but is still being loaded even when I am not using that component. My issue might be different though.

If it's helpful, I found a couple similar threads here: https://github.com/vuetifyjs/vuetify/issues/1561 and https://github.com/vuetifyjs/vuetify/issues/5041 . Based on one of the last responses by @johnleider in the latter issue, it doesn't sound like this is something the team is going to prioritize unless I misread.

johnleider commented 4 years ago

I'm talking with @KaelWD on our decision for not have the v- prefix for those classes but as with everything, there is generally a reason.

I can definitely see how this would be an issue when using Vuetify through a CDN. We'll look into it.

With that being said, the grid is the only section that isn't scoped under .v-application (to my knowledge).

Aaronm14 commented 4 years ago

Yup, with a workaround @KaelWD, I believe, recommended to me in Discord to import Vuetify like import Vuetify from 'vuetify/lib/framework', I was able to get it working the way I as hoping to.

There were still some global styles on things like select, ul etc that were from the combobox component that were breaking other selects I had on the page (had to wrap whole page in v-app instead of just my component because otherwise the dropdown positioning was off). But I just wrote a few overrides for those.

Another developer on my team starting using the grid components yesterday actually, and we came back around to this discussion. I think the crux of the problem is that Vuetify, and others like Bootstrap, aren't the most ideal choice if you're just going to sprinkle certain pieces of it in to an existing app that has some other framework that has similar use cases/functionality. Scoping the styles does go a long way but ultimately doesn't solve all of the problems.

donthijackthegit commented 4 years ago

Same problem here, our legacy system was using bootstrap 3 but need to add Vue components on top of it. I already saw vuetify@2.0.11 added .v-application to scope the selector in, but .row, .col and some other common selector was not and the conflict is still happenning.

My temporary solution is using postcss-prefix-selector and in webpack.config:

...
{
    test: /\.css$/,
    use: [
        require.resolve('style-loader'),
        require.resolve('css-loader'),
        {
            loader: require.resolve('postcss-loader'),
            options: {
                modules: true,
                plugins: () => [
                    prefixer({
                        prefix: '.my-module',
                        transform: (prefix, selector, prefixedSelector) => {
                            if (selector === '.row') {
                                return `${prefix} .row`;
                            } else {
                                return selector;
                            }
                        }
                    })
                ]
            }
        }
    ]
},
...

I have to rely on our QA to review the UI and find the conflicts selectors in both mobile view and desktop view.

@johnleider, as you mentioned

With that being said, the grid is the only section that isn't scoped under .v-application (to my knowledge).

I also found other selectors like button, select, img etc. are not scoped, I am very interested to know why gid and these selectors are not scoped.

johnleider commented 4 years ago

Those changes would be from our reset file.

Sent with GitHawk

donthijackthegit commented 4 years ago

@johnleider , I tried to google what could reset file mean in vuetify but no luck, could you elaborate a bit? Thanks.

nachokb commented 4 years ago

Yup, with a workaround @KaelWD, I believe, recommended to me in Discord to import Vuetify like import Vuetify from 'vuetify/lib/framework', I was able to get it working the way I as hoping to.

@Aaronm14 would you mind elaborating how did that help? I did that but can't avoid needing lots of overrides.

@donthijackthegit just google "CSS Reset"

Aaronm14 commented 4 years ago

@nachokb - So the main culprit is if you use the grid components (col, row, etc). I was just using the Treeview and Menu component, but was still getting the styles for the grid components in dev. I noticed I was not getting those styles when I did the production build, so my code snippet was to have it build the same way on development.

Don't get me wrong, there were still some overrides and things but the main thing for me was not using the grid components on top of our existing bootstrap classes

LasseBorgen commented 4 years ago

Any news on this issue? I'm also running into problems with the flex class (probably part of grid) conflicting with the TailwindCSS flex class.

As previously mentioned, the styles from styles/generic/_reset.scss also seem to apply to global selectors such as button or input.

I've only been able to override the selectors so far.

mkalus commented 4 years ago

I could partly solve this issue by using postcss-prefix-selector. I added this to vue.config.js:

const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');
const prefixer = require('postcss-prefix-selector');

module.exports = {
  configureWebpack: {
    plugins: [
      new VuetifyLoaderPlugin()
    ]
  },
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          prefixer({
            prefix: '.my-wrapper',
            transform: function (prefix, selector, prefixedSelector) {
              if (selector === 'html') {
                return 'html ' + prefix;
              } if (selector === 'body') {
                return 'body ' + prefix;
              } else {
                return prefixedSelector;
              }
            }
          })
        ]
      }
    }
  }
};

Works quite well - fonts and some things are missing, so there is room for improvement.

zeroarst commented 4 years ago

@mkalus @donthijackthegit Thanks!! I use your solution to replace .v-applicatoin .tittle because this causes conflicts with my original .title classes.

mkalus commented 4 years ago

Good to hear, here is an update which catches a few more cases:

module.exports = {
  configureWebpack: {
    plugins: [
      new VuetifyLoaderPlugin()
    ]
  },
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          prefixer({
            prefix: '.my-wrapper',
            transform: function (prefix, selector, prefixedSelector) {
              if (selector.startsWith('.my-exception')) { // skip prefixing here
                return selector;
              }
              if (selector.startsWith('html')) {
                return 'html ' + prefix + selector.substring(4);
              }
              if (selector.startsWith('body')) {
                return 'body ' + prefix + selector.substring(4);
              } else {
                return prefixedSelector;
              }
            }
          })
        ]
      }
    }
  }
};
oprudkyi commented 3 years ago

Hi,

same problem here, but with '.col-' classes and laravel mix

I've tried to use postcss-prefix-selector but without success - postcss applied to all styles and breaks bootstrap as well (replaces selectors indiscriminately) is there any way to process only vuetify ? or any other way to fix this ? out of fixing css by another dummy vue component with correction for styles

mix.js('resources/js/app.js', 'public/js')
  .options({
    postCss: [
      require('postcss-prefix-selector')({
        prefix: '.vuetify-app',
        transform(prefix, selector) {
          if (selector === '.col-12') {
            return `${prefix} .col-12`;
          }
          return selector;
        },
      }),
    ],
  })
 .sass('resources/sass/app.scss', 'public/css');
mkalus commented 3 years ago

I generally wrap my application in a selector - this can be caught in your code, so no need to catch .col12 individually.

To do this, I create a Vue file like this:

<template>
  <div class="my-widget my-wrapper">
    <v-app :id="id">...</v-app>
  </div>
</template>

<style lang="sass">
.my-widget

  &.my-wrapper
    // reset full view - no scroll bars, no full view
    overflow: inherit

    .v-application--wrap
      display: block
      flex: inherit
      min-height: auto
      min-width: inherit
      width: 100%
      overflow-x: hidden

  a.v-btn
    border: inherit
</style>

Might be overspecified a bit, but with this i got I nice widget which did not try to fill the whole screen.

oprudkyi commented 3 years ago

Hi @mkalus there is another sort of problem, vuetify set css globally (VGrid.scss if I correct)

.col-xl, .col-xl-auto, .col-xl-12, .col-xl-11, .col-xl-10, .col-xl-9, .col-xl-8, .col-xl-7, .col-xl-6, .col-xl-5, .col-xl-4, .col-xl-3, .col-xl-2, .col-xl-1, .col-lg, .col-lg-auto, .col-lg-12, .col-lg-11, .col-lg-10, .col-lg-9, .col-lg-8, .col-lg-7, .col-lg-6, .col-lg-5, .col-lg-4, .col-lg-3, .col-lg-2, .col-lg-1, .col-md, .col-md-auto, .col-md-12, .col-md-11, .col-md-10, .col-md-9, .col-md-8, .col-md-7, .col-md-6, .col-md-5, .col-md-4, .col-md-3, .col-md-2, .col-md-1, .col-sm, .col-sm-auto, .col-sm-12, .col-sm-11, .col-sm-10, .col-sm-9, .col-sm-8, .col-sm-7, .col-sm-6, .col-sm-5, .col-sm-4, .col-sm-3, .col-sm-2, .col-sm-1, .col, .col-auto, .col-12, .col-11, .col-10, .col-9, .col-8, .col-7, .col-6, .col-5, .col-4, .col-3, .col-2, .col-1 {
    width: 100%;
    padding: 12px;
}

and there is no way to wrap css

oprudkyi commented 3 years ago

basically I ended with ugly workaround, just after vuetify import dummy component with css that returns global styles back

<template>
  <div>
  </div>
</template>

<script>

/* empty component for fixing css that is broken by Vuetify */

export default {
};
</script>

<style>
.col-xl,
.col-xl-auto,
.col-xl-12,
.col-xl-11,
.col-xl-10,
.col-xl-9,
.col-xl-8,
.col-xl-7,
.col-xl-6,
.col-xl-5,
.col-xl-4,
.col-xl-3,
.col-xl-2,
.col-xl-1,
.col-lg,
.col-lg-auto,
.col-lg-12,
.col-lg-11,
.col-lg-10,
.col-lg-9,
.col-lg-8,
.col-lg-7,
.col-lg-6,
.col-lg-5,
.col-lg-4,
.col-lg-3,
.col-lg-2,
.col-lg-1,
.col-md,
.col-md-auto,
.col-md-12,
.col-md-11,
.col-md-10,
.col-md-9,
.col-md-8,
.col-md-7,
.col-md-6,
.col-md-5,
.col-md-4,
.col-md-3,
.col-md-2,
.col-md-1,
.col-sm,
.col-sm-auto,
.col-sm-12,
.col-sm-11,
.col-sm-10,
.col-sm-9,
.col-sm-8,
.col-sm-7,
.col-sm-6,
.col-sm-5,
.col-sm-4,
.col-sm-3,
.col-sm-2,
.col-sm-1,
.col,
.col-auto,
.col-12,
.col-11,
.col-10,
.col-9,
.col-8,
.col-7,
.col-6,
.col-5,
.col-4,
.col-3,
.col-2,
.col-1 {
  padding: 0 15px;
}

.row {
  margin-right: -15px;
  margin-left: -15px;
}
</style>
e-bxbx commented 3 years ago

Any news on this issue?

erropix commented 3 years ago

I am using vuetify@2.2.11 and still many component styles aren't prefixed, like the v-app-bar, I don't know why there is no option to make all the styles nested under #app or something.

MartB commented 3 years ago

Yeah .container is a worst offender, trying to add vuetify components to a legacy app that cant be re-written is a pain with these global classes.

Kinda my fault, but maybe this could be changed.

ZeroThe2nd commented 3 years ago

Actually having the same issue as @MartB, trying to integrate Vuetify components into a (legacy) application, and the CSS reset is kinda screwing things over... I did manage to filthy-hack-fix this by un-commenting the import of main.sass, then to manually import that file except the reset file (which I @import-ed inside a .v-reset class) and that basically solves the problem, but that requires editing the source files, so that kinda rules it out.

Would it be an idea to add an option inside the Vuetify options configuration to skip/prefix the reset include so one can include the file themselves somewhere else?

ZeroThe2nd commented 3 years ago

A little journey down the rabbit hole: It's possible to (in a very very disgusting way) to automatically have the sass include removed. (did I already say it was hella disgusting?). Note that the following solution might be considered NSFW:

I've managed to handle this based on a tiny custom webpack plugin, that edits the source code to remove the line in question. You can find it over here if you really really need it!

Note that I do recommend to add an include of the reset file like such to ensure Vuetify keeps functioning properly:

// I'm using a data-tag as example, though use whichever scoped selector you like.
[data-vuetify] {
    @import '~vuetify/src/styles/generic/reset.scss';
}

I'll remove this disgusting workaround piece of code once Vuetify ships with a way to fix this another way.

Forgive me programmers, for I have sinned...

lmcsu commented 3 years ago

I'm using Vuetify with vue-cli inside of a legacy app (with bootstrap, etc) and faced the same problem. Here's my solution, partly based on your answers guys, thanks. For vue-cli only! I'm not advanced in chainWebpack, maybe I made something wrong and ugly, but it works the right way for me.

1) add "data-vuetify" attribute to your root element. 2) add "postcss-prefix-selector" to your packages list and require it in vue.config.js const prefixer = require('postcss-prefix-selector'); 3) use chainWebpack to prefix all sass rules inside of vuetify/src

chainWebpack: (config) => {
    const sassRule = config.module.rule('sass');
    const sassNormalRule = sassRule.oneOfs.get('normal');
    // creating a new rule
    const vuetifyRule = sassRule.oneOf('vuetify').test(/[\\/]vuetify[\\/]src[\\/]/);
    // taking all uses from the normal rule and adding them to the new rule
    Object.keys(sassNormalRule.uses.entries()).forEach((key) => {
        vuetifyRule.uses.set(key, sassNormalRule.uses.get(key));
    });
    // moving rule "vuetify" before "normal"
    sassRule.oneOfs.delete('normal');
    sassRule.oneOfs.set('normal', sassNormalRule);
    // adding prefixer to the "vuetify" rule
    vuetifyRule.use('vuetify').loader(require.resolve('postcss-loader')).tap((options = {}) => {
        options.sourceMap = process.env.NODE_ENV !== 'production';
        options.plugins = [
            prefixer({
                prefix: '[data-vuetify]',
                transform(prefix, selector, prefixedSelector) {
                    let result = prefixedSelector;
                    if (selector.startsWith('html') || selector.startsWith('body')) {
                        result = prefix + selector.substring(4);
                    }
                    return result;
                },
            }),
        ];
        return options;
    });
    // moving sass-loader to the end
    vuetifyRule.uses.delete('sass-loader');
    vuetifyRule.uses.set('sass-loader', sassNormalRule.uses.get('sass-loader'));
},
chrisZng commented 3 years ago

Any news on this issue?

alexkleinwaechter commented 3 years ago

@Imcsu: Thanks for this one! This really saved me. One hint for people who try this: surround your tag with a div and place the parameter there.

  <div data-vuetify>
    <v-app>
    </v-app>
  </div>
uke5tar commented 3 years ago

@Imcsu You sir are a genius! Thank you so much!

KaelWD commented 3 years ago

Grid classes have been scoped with a v- prefix in v3 (#7039). There are no plans to scope other utility classes at this time.

code4jhon commented 2 years ago

Those changes would be from our reset file.

@johnleider is there a way to avoid loading that file ?

johnleider commented 2 years ago

We kindly ask users to not comment on closed/resolved issues. If you believe that this issue has not been correctly resolved, please create a new issue showing the regression or create a new discussion.

If you have any questions, please reach out to us in our Discord community.