vuejs / rfcs

RFCs for substantial changes / feature additions to Vue core
4.87k stars 546 forks source link

SFC style CSS variable injection (new edition) #231

Closed yyx990803 closed 3 years ago

yyx990803 commented 3 years ago

This is an improved alternative of the previous version (<style vars> as proposed in #226)

Thanks to feedback by @Demivan and @privatenumber, #226 has a few notable issues that can be improved:

  1. Requires manual vars declaration for exposing variables that can be used.
  2. No obvious visual hint that a variable is injected and reactive
  3. Different behavior in scoped/non-scoped mode.
  4. In non-scoped mode, the CSS variables would leak into child components.
  5. In scoped mode, the global: prefix is required to use normal CSS variables declared outside of the component. It would be preferable that normal CSS variable usage stays the same in/out of the component.

This proposal addresses all of the above with the following usage:

<template>
  <div class="text">hello</div>
</template>

<script>
  export default {
    data() {
      return {
        color: 'red',
        font: {
          size: '2em'
        }
      }
    }
</script>

<style>
  .text {
    color: v-bind(color);

    /* expressions (wrap in quotes) */
    font-size: v-bind('font.size');
  }
</style>

Summary:

Rendered

edisdev commented 3 years ago

Amazing. Is SCSS compatible with this feature?

@m4heshd I tried this. This feature worked with SCSS. But I couldn't get this feature to work in production mode. So when I run vue cli server build it didn't work. What do you think about that? @yyx990803

https://user-images.githubusercontent.com/21293903/124126368-49e13900-da83-11eb-9dc2-94ec2a69e33e.mov

<template>
  <div class="Example">
    <div class="area">
      <div class="form">
        <label>Select Color (SCSS)</label>
        <input type="color" v-model="themeColors.bgColor" />
      </div>
      <div class="preview">
        <span>{{ themeColors.bgColor }}</span>
        <div class="customColor"></div>
      </div>
    </div>
  </div>
</template>

<script>
import { ref } from "vue";

export default {
   setup() {
    const themeColors = ref({
      bgColor: ''
    })

    return {
      themeColors
    };
  },
};
</script>

<style lang="scss">

.Example {
  $customColor: v-bind('themeColors.bgColor');

  .customColor {
    background: $customColor;
    width: 100px;
    height: 50px;
  }
}

</style>
m4heshd commented 3 years ago

@ITenthusiasm good to know that.

@edisdev Ouch. 😬 That's what I was afraid of. I really don't wanna be hit with any "gotchas" when going into production. Did you use dart-sass or node-sass?

edisdev commented 3 years ago

@m4heshd Yes, project dependencies has node-sass and sass-loader. Actually this feature is not working also with pure css in production mode. While reviewing this, I found that the style variable name that binds to the element is incorrect in production mode.

For Example : Screen Shot 2021-07-01 at 17 39 56

--7ba5bd90-themeColors_bgColor is not correct. I guess that's the variable name prepared for development mode.

m4heshd commented 3 years ago

@edisdev I don't think that's a problem because there's a bunch of unique identifiers and some extra stuff added in the build process. But you should try sass (dart-sass) instead of node-sass because it's known to have a lot of incompatibilities and it's deprecated. sass-loader works with both of them.

Oops. Just took a look again and saw what's wrong. Way too sleep deprived to be properly conscious. 😩 So it looks like this feature is definitely not production ready and we shouldn't be adding any functionality dependent on it.

edisdev commented 3 years ago

@m4heshd I hope it will be fixed soon. I opened issue about this.

GuoChen-thlg commented 3 years ago

When I tried to use it in a development environment, it worked fine

:root{
     --47536436-theLen: 200px;
    --47536436-angle: 45deg;
    --47536436-offset: 100px;
}
.style{
    position: relative;
    list-style-type: none;
    width: var(--47536436-theLen);
    height: var(--47536436-theLen);
    transform: rotate3d(0.7, 0.5, 0.5, var(--47536436-angle));
    transform-style: preserve-3d;
}

But when I packed it, it didn't work

:root{
   --47536436-theLen: 200px;
   --47536436-angle: 45deg;
   --47536436-offset: 100px;
}
.style{
    position: relative;
    list-style-type: none;
    width: var(--946a2296);
    height: var(--946a2296);
    transform: rotate3d(.7,.5,.5,var(--8afc983c));
    transform-style: preserve-3d;
}
GulnazKhasanova commented 3 years ago

Tell me how to dynamically change a property background-image in ::before?

yyx990803 commented 3 years ago

Looks like most of the issues raised after the final comments period were about implementation bugs - most of which have been addressed in the latest version (3.1.5).

Since these are all implementation issues, the RFC itself is considered finalized. We will try to resolve all implementation bugs in the upcoming 3.2 and this feature will be out of experimental status when 3.2 stable is released.

LiuQixuan commented 3 years ago

How to prevent the rendered css variable with hash prefixed? The same component will render multiple css files, which is obviously unreasonable. I am not afraid of same css variable name.In my mind,the css variable was written in the HTML element and only works for the css var( ) syntax under the HTML element. Why do you need to add a hash prefix? Just to avoid duplication?

iDerekLi commented 3 years ago

BUG: in SCSS

System Info

  OS: Windows 11
    CPU: (8) x64 AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
    Memory: 535.72 MB / 6.94 GB
  Binaries:
    Node: 12.15.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.5 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22000.1.0), Chromium (91.0.864.67)
    Internet Explorer: 11.0.22000.1
  npmPackages:
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2"
  vueCLI:
     @vue/cli 4.5.13

CSS

css parsed variables are normal.

<script setup>
const width = 123;
</script>
<style scoped>
.canvas {
  position: relative;
  width: v-bind(width + "px");
  box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.2);
  background: #ffffff;
}
</style>

image

SCSS

scss parsing variables are not equal.

<script setup>
const width = 123;
</script>

<style lang="scss" scoped>
.canvas {
  position: relative;
  width: v-bind(width + "px");
  box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.2);
  background: #ffffff;
}
</style>

image

berzi commented 3 years ago

v-bind() doesn't seem to work inside CSS rules with the ::v-global() selector.

Is this the right place to report this? I got here through a warning in the compiler.

eyun-tv commented 2 years ago

v-bind() doesn't seem not work i use pug & coffeescript and stylus

image

image

privatenumber commented 2 years ago

@iDerekLi @eyun-tv

In both of your examples, you're using preprocessors that manipulate the source code before passing it to Vue. That means Stylus or SCSS is likely changing the JS expression you pass inside v-bind. In the RFC, it says expressions should be wrapped in quotes, and that will probably prevent the preprocessors from interfering too: v-bind('vol + "%"')

FYI, I don't think this is the right place to make bug reports or seek help, so ideally you submit an issue instead of responding here.

eyun-tv commented 2 years ago

image I don't think that's the problem you're talking about I made the changes as you said, but it still doesn't work I see that the variable is generated in the generated css, but there is no place to initialize this variable @privatenumber

andrei-gheorghiu commented 2 years ago

@eyun-tv, according to this post above, you should not report implementation bugs on this thread. Please create a separate issue on the vue repo.

Also note, starting tomorrow, vue will default to version 3. You might want to wait until the move is completed and post on the appropriate renamed repo.

L1yp commented 1 year ago

@eyun-tv, according to this post above, you should not report implementation bugs on this thread. Please create a separate issue on the vue repo.

Also note, starting tomorrow, vue will default to version 3. You might want to wait until the move is completed and post on the appropriate renamed repo.

same problem, I guess it was caused by teleported.

L1yp commented 1 year ago

repo url

deepthan commented 1 year ago

Post a method, have not found for a long time to try out, I hope it can help you.

css background: src(xxx) uses variables to concatenate urls in js

<script setup lang="ts">
const imgUrl = ref(
  'url(https://p3-passport.byteimg.com/img/mosaic-legacy/3795/3047680722~180x180.awebp)'
)
</script>

<style lang="less" scoped>
.compass {
  background: v-bind(imgUrl);
}
</style>
deepthan commented 1 year ago

How can i to bind a variable of @font-face src ? The following does not work.

<script setup lang="ts">
const fontFamily = ref(
  'url(http://www.xxx.comxxx.ttf)'
)
</script>

<style lang="less">
.@font-face {
  font-family: 'YouSheBiaoTiHei';
  src: v-bind(fontFamily);
}
</style>