rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

SFC scoped style improvements

Open yyx990803 opened this issue 6 years ago • 66 comments

We are aware that the current SFC scoped style implementation has a number of issues/limitations regarding the component root node and slotted content. I'd like to use this thread to collect feedback and help us improve the scoped style implementation.

What are the pain points when using <style scoped>? What features you hope <style scoped> could support? We have some ideas, but we'd like to hear from the users to get a clearer picture. If you have ideas or suggestions, please provide feedback here (and please be as specific as possible, with code samples and clear descriptions of your scenario).

yyx990803 avatar Nov 08 '19 19:11 yyx990803

The main issue we face is with multiple nested functional components, scoping doesn't seem to apply.

Removing that limitation itself removes tonnes of limitation

blake-newman avatar Nov 08 '19 19:11 blake-newman

Scoped by default. It would not be backwards compatible unless there were a configuration flag. I think it is beneficial because I've never had a component that wasn't scoped. I put global styles in an actual CSS file at the top level.

parker-codes avatar Nov 08 '19 19:11 parker-codes

This is interesting.. I've never used scoped and instead rely on BEM-like naming conventions to avoid collisions.. this affords me the flexibility for a parent component to tweak its children's styles with actual css rather than creating all kinds of attributes that provided limited customization.

I thought this was the intended usage given this note: Scoped styles do not eliminate the need for classes. https://vue-loader.vuejs.org/guide/scoped-css.html#also-keep-in-mind

michaeldrotar avatar Nov 08 '19 19:11 michaeldrotar

Scoped by default. It would not be backwards compatible unless there were a configuration flag.

There is an alternatives to scoped css (css modules, css in js etc...) so this cannot be a default option without conflicting against those alternatives.

nblackburn avatar Nov 08 '19 19:11 nblackburn

Making SCSs the default, then generating a unique class per component to wrap around all classes in that component,


Advantage is,

A more cleaner html will be generated(html might not be touched)

This is actually the real scoping

The class generated for each component may follow a pattern which developer can manually target

Wharley01 avatar Nov 08 '19 19:11 Wharley01

There are several CSS-in-JS libraries in the React ecosystem that offer a couple of nice features. styled-components, for example, lists compelling points in its motivation.

Especially, I always wondered whether Vue could provide something along those lines:

  • Simple dynamic styling: adapting the styling of a component based on its props or a global theme is simple and intuitive without having to manually manage dozens of classes.

Of course, you can bind styles to a computed property in Vue but having the computation and adaption of styles right inside of a styled-component always made more sense to me personally.

Instead of passing classes for styles, those components are in fact really just components™ and are accessed like any old component: by its name. That makes templates more readable IMO. Also, having a global theme (with colors, spacings et al.) at hands inside of such structures often comes in handy.

There are adapters for e.g. emotion but it would be really cool to have something like this "battery included" in Vue itself. I'm definitely not saying to replace the current way of doing things but instead to take a look into the ideas as I'm convinced they have a lot to offer.

hollandThomas avatar Nov 08 '19 19:11 hollandThomas

Coming from an Angular background, a thing I really miss in Vue SFC is the :host selector. 95% of time I need to stile the root component, having to define a class every time is just annoying...

IlCallo avatar Nov 08 '19 19:11 IlCallo

A few things I have in mind:

  • Ability to target root nodes with a custom selector, I believe angular is doing that.
  • It could be worth making scoped styles are the default as it is extremely unlikely to have a component targeting styles for other components and should be discouraged as well, I have built quite a few apps over the past years and never I have not used scoped styles.
  • If we can find a better alternative than the data-* selectors that would be great, maybe even make it predictable like a wrapper class name equal to the name of the component.

logaretm avatar Nov 08 '19 20:11 logaretm

We should consider moving attribute selectors to simple class selectors button.hash vs button[hash]

I haven't tested it, but I believe attribute selectors are some of the slowest around.

image

hecktarzuli avatar Nov 08 '19 20:11 hecktarzuli

If we can find a better alternative than the data-* selectors that would be great.

We should consider moving attribute selectors to simple class names button.hash vs button[hash]

@hecktarzuli, @logaretm @Wharley01 You can use CSS modules if you are like me and don't like data attributes polluting the DOM and exposing the underlying framework.

nblackburn avatar Nov 08 '19 20:11 nblackburn

@nblackburn I'm fine with them making my DOM ugly, also I wouldn't care about ppl finding out my framework of choice. I'm only concerned by the selector performance which @hecktarzuli has illustrated.

logaretm avatar Nov 08 '19 20:11 logaretm

I never liked scope style my components because I usually needed to reuse some of the stylings in other places. Also, CSS is hard to maintain and have pieces of it in each component wasn't ideal for me. I then started using utility-first css, and I longer feel like I need css in SFC anymore.

4refael avatar Nov 08 '19 20:11 4refael

I don't know if its currently available or if it's doable but it would be great to reactively use CSS variables in the template.

<template>
   <div :class="background: '--chosen-bg'"></div>
</template>

Rolanddoda avatar Nov 08 '19 20:11 Rolanddoda

:boom: The main problem with Vue styles is it's hard to have reusable styles that are only loaded when they are needed.

It seems pretty darn hard to write style XYZ, and that's used by 5 components without shoving it somewhere global (which forces it to be loaded even when it's not needed)

@yyx990803 if you could find a way to make reusable styles that are required by components, but only loaded once, on-demand, that would be huge step forward.

hecktarzuli avatar Nov 08 '19 20:11 hecktarzuli

I don't know if its currently available or if it's doable but it would be great to reactively use CSS variables in the template.

<template>
   <div :class="background: '--chosen-bg'"></div>
</template>

You can do things like this:

<div style="--some-color: blue"></div>
div {
  color: var(--some-color, 'red');
}

Replace style with a :style="myStyles" binding, make myStyles a computed to combine multiple attributes into css variables, and then good to go.

Personally I think it's alot easier to not used scoped css and just use normal css to target and change styles as needed -- but this is a flexible option for people that want it.

More info:

  • https://www.telerik.com/blogs/passing-variables-to-css-on-a-vue-component
  • https://vuedose.tips/tips/theming-using-custom-properties-in-vuejs-components

michaeldrotar avatar Nov 08 '19 20:11 michaeldrotar

I haven't tested it, but I believe attribute selectors are some of the slowest around.

Didn't this stop being a real problem ages ago? Performance is certainly important, but I'd like to see some actual data to back up that claim.

casey6 avatar Nov 08 '19 20:11 casey6

Would be nice to somehow be able to inject reactive variables using for example a 'style' property on the vie component. I find the above example a bit cumbersome. We could do something like this:

<script>
export default {
  styles() {
    return {
      $primary: '#222266'
    }
  }
}
</script>

<style>
.btn {
  background-color: $primary;
}
</style>

We could make it behave the same as computed variables. It would be very powerful to have design systems inject their variables to for example a theme component

chris-visser avatar Nov 08 '19 20:11 chris-visser

I used to think that scoped means that it would only apply to the component, that it was used in. But it does not. Sometimes it removes the css from the component instead. Either improve documentation or behavior.

ezgif-2-906ed815a0ca 1

ThomasWT avatar Nov 08 '19 20:11 ThomasWT

I used to think that scoped means that it would only apply to the component, that it was used in. But it does not. Sometimes it removes the css from the component instead. Either improve documentation or behavior.

ezgif-2-906ed815a0ca 1

@ThomasWT You have global selectors like html in that code which will not work with scoped css.

nblackburn avatar Nov 08 '19 20:11 nblackburn

I used to think that scoped means that it would only apply to the component, that it was used in. But it does not. Sometimes it removes the css from the component instead. Either improve documentation or behavior. ezgif-2-906ed815a0ca 1

You have global selectors like html in that code which will not work with scoped css.

Will that remove most, if not all the CSS?

ThomasWT avatar Nov 08 '19 20:11 ThomasWT

I used to think that scoped means that it would only apply to the component, that it was used in. But it does not. Sometimes it removes the css from the component instead. Either improve documentation or behavior. ezgif-2-906ed815a0ca 1

You have global selectors like html in that code which will not work with scoped css.

Will that remove most, if not all the CSS?

If you don't organise your CSS correctly then yes. The CSS must be specific to the component it is defined in for it to work. Global styles will need to be defined elsewhere.

nblackburn avatar Nov 08 '19 20:11 nblackburn

I personally have found it to be a very good trade-off using scoped styles, even though it means duplicating CSS many times between components (find/replace across files is fairly practical for making consistent changes if you use smart naming conventions).

In contrast getting the same dynamic styles to be consistent across many components feels painful. In our case, individual customers set themes/colors that apply across many components: we end up injecting their config into global JS and then using a mixin to construct the correct CSS styles in various components, bypassing the scoped classes (which feels like where this should actually go) and injecting the dynamic styles directly on the DOM elements.

Meekohi avatar Nov 08 '19 21:11 Meekohi

Use class prefixing as a scoping mechanism instead of attributes? That would remove performance penalty in runtime. Dynamic classes could be implicitly prefixed in runtime.

Also render functions are left out of this feature, maybe something could be improved in that regard?

CyberAP avatar Nov 08 '19 21:11 CyberAP

For backwards compatibility, there could be v-bind:style for those still using it, but a new vue directive for default, component-scoped css like v-style which can take an object or template literals like styled-components so it can have conditionals in the css itself. 🙂

Was thinking this because v-bind can move more towards data instead of the design of a component as well.

Then anything in

ahtee avatar Nov 08 '19 21:11 ahtee

I’d like to see a way to use dynamic styles other than relying on a predefined :class binding or :style binding.

I know this can be done with CSS-in-JS libraries like Vue-styled-components, but none of them seem to have solid support for Vue.

NvdB31 avatar Nov 08 '19 22:11 NvdB31

Please tell (in documentation) about the >>> operator, many developers do not know about its existence, but it solves almost all problems with scoped styling.

alex-lit avatar Nov 08 '19 22:11 alex-lit

Please tell (in documentation) about the >>> operator, many developers do not know about its existence, but it solves almost all problems with scoped styling.

@alex-lit It's in the vue-loader docs https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors

hecktarzuli avatar Nov 08 '19 22:11 hecktarzuli

I’d like to see a way to use dynamic styles other than relying on a predefined :class binding or :style binding.

I know this can be done with CSS-in-JS libraries like Vue-styled-components, but none of them seem to have solid support for Vue.

How about css variables?

<template>
  <section class="my-component" :style="`--my-var: ${myVar};`">
    <span>
      Hello, World!
    </span>
  </section>
</template>

<script>
export default {
  data() {
    return {
      myVar: 'red',
    };
  },

  mounted() {
    setTimeout(() => {
      this.myVar = 'green';
    }, 1000);
  },
};
</script>

<style>
.my-component {
  position: relative;
  display: flex;
  width: 100vw;
  height: 100vh;
  align-items: center;
  justify-content: center;
  background-color: var(--my-var);
  color: white;
  font-family: monospace;
  font-size: 48px;
  transition-duration: 1s;
}
</style>

alex-lit avatar Nov 08 '19 22:11 alex-lit

Deterministic scoped selectors values would be great, maybe even a way to define how the data-v value should be calculated. The webpack css-loader enables us to do so for CSS modules with their localIdentName option. Would be nice to see this for Vue scoped styles too.

This comes in handy when you have multiple webpack jobs that should produce compatible style / components.

rico-ocepek avatar Nov 08 '19 23:11 rico-ocepek

The most challenging problem I face is when I use a library which has its own scoped styles (or even my own components). Trying to modify the default styles of those components is truly a big pain for me. I also think that as css houdini and some other people here propose, the future will be js in css. It would really be useful to use variables declared in script in the style section

korkies22 avatar Nov 08 '19 23:11 korkies22