Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to define style within a web component? #12

Open
zhaomenghuan opened this issue Apr 7, 2018 · 33 comments
Open

How to define style within a web component? #12

zhaomenghuan opened this issue Apr 7, 2018 · 33 comments

Comments

@zhaomenghuan
Copy link

I tried to use this plugin to write a UI library, but I found that the styles defined in the component did not take effect, and the template tag in the .vue file could not write style in it, and how to handle it more elegantly.

@gnardecky
Copy link

gnardecky commented Apr 19, 2018

Playing around with this awesome wrapper, I'm finding it impossible to attach any style to my registered vue converted webcomponent.

  1. <style module> makes no difference to adding ANY style.
  2. ONLY inline style works

How can I define ANY style on my shadow DOM (from FILE 2) with this wrapper?

File 1:

<template>
  <div>
    <!-- test row-template outside of zing-grid tag -->
    <row-template branch="hello world" username="nardecky" commitId="023741as" runTime="0:27"></row-template>
  </div>
</template>

<script>
import RowTemplate from '@/components/row-template'
import wrap from '@vue/web-component-wrapper'
import Vue from 'vue'

// define custom row template immediately as a web component
const CustomElement = wrap(Vue, RowTemplate);
window.customElements.define('row-template', CustomElement);

// define templateing component
export default {
  data () {
    return {
    }
  },
}
</script>

<!-- style scoped globally or not, doesn't work for second example -->
<style>

/* works */
row-template {
  display: flex;
  justify-content: space-between;
  background: #eee;
  margin-bottom: 1rem;
  padding: 1rem;
  border-radius: .5rem;
  overflow: hidden;
}

/* Doesn't work*/
 row-template >>> .customSingleRow{
  display: flex;
  justify-content: space-between;
  background: #eee;
  margin-bottom: 1rem;
  padding: 1rem;
  border-radius: .5rem;
  overflow: hidden;
}
</style>

File 2:

<template>
  <section class="customSingleRow">
    <div class="firstCell">
      <div class="buildStatus"></div>
      <div>

        <h3>{{ branch }}</h3>
        <div class="build-contents">
            <span>{{ username }}</span>
            <span>{{ branch }}</span>
            <span>{{ commitId }}</span>
            <span>{{ runTime }}</span>
        </div>
      </div>
    </div>
    <div>
      <select>
        <option>Restart Build</option>
        <option>Reset Cache</option>
        <option>Drill Into Page</option>
      </select>
    </div>
  </section>
</template>

<script>
export default {
  name: 'row-template',
  props: ['branch', 'username', 'commitId', 'runTime'],
  data () {
    return {
    }
  },
  mounted: function() {
    console.log(this.$style)
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
/* trying to get any selector to work for this component */
:host >>> .customSingleRow,
:host-context(section),
:host, 
row-template,
.customSingleRow {
  display: flex;
  justify-content: space-between;
  background: #eee;
  margin-bottom: 1rem;
  padding: 1rem;
  border-radius: .5rem;
  overflow: hidden;
}

/* ideally I want to just target .customSingleRow*/
</style>

@gnardecky
Copy link

I found a really hacky solution.

Reasoning: The reasoning for this solution is styles are handled by webpack. They are not accessible on the Vue element itself, thus it can't be solved with this wrapper correct?

Implementation

Create a custom component to render/import style tags since the compiler won't let us

File 1:

<template>
  <section>
    <custom-styles>
      .custom-single-row {
        display: flex;
        justify-content: space-between;
        background: #eee;
        margin-bottom: 1rem;
        padding: 1rem;
        border-radius: .5rem;
        overflow: hidden;
      }
    </custom-styles>

    <div class="custom-single-row">
      
      <div class="first-cell">
        <div v-bind:class="buildStatus"></div>
        <div>
          <h3>{{ branch }}</h3>
          <div class="build-contents">
              <span>{{ username }}</span>
              <span>{{ branch }}</span>
              <span>{{ commitId }}</span>
              <span>{{ runTime }}</span>
          </div>
        </div>
      </div>
      <div>
        <select>
          <option>Restart Build</option>
          <option>Reset Cache</option>
          <option>Drill Into Page</option>
        </select>
      </div>
    </div>

  </section>
</template>

<script>
import CustomStyles from '@/components/styles'
export default {
  name: 'row-template',
  props: ['status', 'branch', 'username', 'commitId', 'runTime'],
  data () {
    return {
    }
  },
  mounted: function() {
    console.log(this)
  },
  computed: {
    buildStatus: function() {
      return `build-status build-${this.status}`;
    }
  },
  components: {
    'custom-styles': CustomStyles
  }
}
</script>


File two is our <custom-styles> element:

<template>
  <section ref="styles" style="display:none;">
    <slot></slot>
  </section>
</template>

<script>
export default {
  name: 'custom-styles',
  props: [],
  data () {
    return {
    }
  },
  mounted: function() {
    console.log(this.$refs.styles)
    let styleTag = document.createElement('style');
    styleTag.textContent = this.$refs.styles.textContent;
    this.$refs.styles.textContent = null;
    this.$refs.styles.appendChild(styleTag);
  }
}
</script>

This will import our styles into our webcomponent, making them scoped.

screen shot 2018-04-19 at 5 13 36 pm
screen shot 2018-04-19 at 5 13 24 pm

@nate250
Copy link

nate250 commented Apr 23, 2018

I slightly simplified @gnardecky's custom style component like so:

const customStyle = {
  name: 'custom-style',
  render (createElement) {
    return createElement('style', this.$slots.default)
  }
}

@ankurk91
Copy link
Contributor

ankurk91 commented Apr 28, 2018

I was able to enable shadow mode with webpaack v4 and vue-loader 15 (without vue-cli v3), docs

// webpack.config.js
 module: {
    rules: [

{
        test: /\.vue$/,
        loader: 'vue-loader',
        exclude: path.resolve(__dirname, 'node_modules'),
        options: {
           shadowMode: true // vue-loader v15.0.9+
         }
      },
 {
        test: /\.css$/,
        use: [
          {
            loader: 'vue-style-loader',
            options: {
              shadowMode: true
            }
          },
          'css-loader',
          // don't use `style-loader`
        ],
      },
]
]

Now i can use vue SFC as normal

<template>
<div class="card">
<!-- add html here -->
</div>
</template>

<script>
// add script
</script>

<style>
/* no need to use CSS Modules or Scoped CSS */
@import "~bootstrap/dist/css/bootstrap.css";
.card {
   background: red
}
</style>

You also need to load import *.vue files like this, (only vue-loader >v15.0.9)

//app.js
import CustomElement from './CustomElement.vue?shadow'

Now vue-loader injects style tag within shadowRoot not in document head.

@victorhramos
Copy link

@ankurk91 i tried the solution you told but still goint to head? can you provide more details please?

@victorhramos
Copy link

@ankurk91 yay, i got it... now wondering how to make it with <style lang="scss">

@maciekkus
Copy link

@victorhramos hi, can you share some details why it wasn't working for you and now it works?
I have the same problem you've had probably - styles are not in shadow but in head.

@victorhramos
Copy link

@maciekkus here one working example https://github.com/victorhramos/vuew-shadowdom

@maciekkus
Copy link

@victorhramos thanks.
btw. I've managed somehow to get it working.
but it seems that for me shadow styles do not work in safari at all, and in firefox global styles leak to webcomponent.
Is it just the current state of that technology and browser support state?

@lgt
Copy link

lgt commented Dec 9, 2018

is there any good workaround to use properly scoped scss?

@QuentinouLeTwist
Copy link

QuentinouLeTwist commented Dec 11, 2018

Good initiative this project, I was unfortunately unable to use with bootstrap-vue :(
Solution from @victorhramos did not really work on my side. Styles are still in the head.

@rsmdc
Copy link

rsmdc commented Mar 14, 2019

I wonder like @victorhramos why css dosen’t work in case <style lang=“scss”>.
but I found how to work css.
I'm using vue-cli 3, maybe it will be helpful.

↓my code.

vue.config.js

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          options.shadowMode = true
          return options
        })
    config.module
      .rule('css')
      .oneOf('vue')
      .use('vue-style-loader')
        .tap(options => {
          options.shadowMode = true
          return options
        })
    config.module
      .rule('scss')
      .oneOf('vue')
      .use('vue-style-loader')
        .tap(options => {
          options.shadowMode = true
          return options
        })
    },
  css: {
    modules: true,
   // I'm using material-components-web, this option is to use it.
    loaderOptions: {
      sass: {
        includePaths: ['node_modules']
      }
    }
  }
}

@andreasvirkus
Copy link

andreasvirkus commented Apr 1, 2019

Also having trouble extracting scss styles from SFC-s... @rsmdc have you made any progress?

Further debugging shows that the styles from stand-alone .scss/.css files and my App.vue's <style lang="scss"> block are loaded, but not from other imported components.

@andreasvirkus
Copy link

@ankurk91 Do you have any reference to the ?shadow parameter you append to your *.vue files import? Can't find it mentioned anywhere (neither in vue-loader docs nor in their source code)

@ankurk91
Copy link
Contributor

ankurk91 commented Apr 2, 2019

@andreasvirkus

The undocumented ?shadow query parameter was removed in this commit vuejs/vue-loader@4529f83

@gatherben
Copy link

Thanks to @andreasvirkus for offering the very counterintuitive solution of moving to external style files. I was tearing my hair our trying to figure out what I was doing wrong and switching to standalone files solved the problem. Does anyone understand why this works? Seems like the source of the scss/css shouldn't matter to webpack... FYI I'm using scss without issue using CLI 3.

@andreasvirkus
Copy link

andreasvirkus commented May 7, 2019

No, I've yet to solve this properly :/
I did discover that for some files, switching from an alias (import Component from '@/compnents/Component') to a relative path (... from '../components/Component) did the trick, but again - very odd and non-consistent behaviour 🤔

@PsySolix
Copy link

Any progress on this one? Seem to be an issue for more people..
Could also be that we are not understanding the fundamentals of building the WC..

@chwassme
Copy link

I did discover that for some files, switching from an alias (import Component from '@/compnents/Component') to a relative path (... from '../components/Component) did the trick, but again - very odd and non-consistent behaviour 🤔

Did you remark the the typo in the "alias-import" 😉

@janwirth
Copy link

janwirth commented Nov 4, 2019

This problem made me switch to https://github.com/karol-f/vue-custom-element

@ccarstens
Copy link

@FranzSkuffka Are you adding css to the component via the "shadowCss" option? Or does it work for you to use the <style> tag in the SFC?

@ccarstens
Copy link

@FranzSkuffka never mind, vue-custom-element is working for me too :)

@gihandilanka-github
Copy link

@FranzSkuffka never mind, vue-custom-element is working for me too :)

How did you add the styles using shadowCss options or <style> tag in the SFC? please help

@SergkeiM
Copy link

SergkeiM commented Apr 7, 2021

@gihandilanka-github
Copy link

@gihandilanka-github karol-f/vue-custom-element#238

After I adding following code

<style lang="scss">
	.myClass{
		color: red;
	}
</style>

I got the following error
jserror

After I removing the lang="scss" part js compile error not happen but still not luck for my issue

@gihandilanka-github
Copy link

gihandilanka-github commented Apr 7, 2021

@Froxz I tried with your example project in git . Now working fine as you told. Instead of writing styles with in <style> tags in the component, is there a way to link external css file.

@SergkeiM
Copy link

SergkeiM commented Apr 7, 2021

Hi @gihandilanka-github

sure just write

<style lang="scss">
	@import "path/to/css";
</style>

@hzmsrv
Copy link

hzmsrv commented Oct 26, 2022

i found the :root{--xxx} in web components are not working? are there any one resolve this problem?

@rajeevverma076
Copy link

When I'm trying to publish multiple web components using the vue cli command vue-cli-service build --target wc --inline-vue --name my-element src/*.vue. it is building perfectly fine but external dependencies like CSS and javascript file is not importing in the build.

Can anyone help me with how to build web components with all external dependencies? import all CSS from node_modules as a dependency.

@rajeevverma076
Copy link

Hi @gihandilanka-github

sure just write

<style lang="scss">
	@import "path/to/css";
</style>

Not working

@rajeevverma076
Copy link

@Froxz I tried with your example project in git . Now working fine as you told. Instead of writing styles with in <style> tags in the component, is there a way to link external css file.

I have tried this method as well but still not working. could you please provide me solution with third-party library as well.

@rajeevverma076
Copy link

module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.shadowMode = true
return options
})
config.module
.rule('css')
.oneOf('vue')
.use('vue-style-loader')
.tap(options => {
options.shadowMode = true
return options
})
config.module
.rule('scss')
.oneOf('vue')
.use('vue-style-loader')
.tap(options => {
options.shadowMode = true
return options
})
},
css: {
modules: true,
// I'm using material-components-web, this option is to use it.
loaderOptions: {
sass: {
includePaths: ['node_modules']
}
}
}
}

it is not working. Do you know if you have working repo?

@EranGrin
Copy link

@rajeevverma076 We are working on a solution that might solve your issue, but currently there is no support for Vue CLI
https://www.npmjs.com/package/vue-web-component-wrapper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests