CSS variables use in Vue - css

Is it possible to use pure CSS variables with Vue without having to link any stylesheets or use SASS/PostCSS? Unsure why I'm unable to get this to work in its most basic form.
<template>
<div id="test">
TEST
</div>
</template>
<style scoped>
:root {
--var-txt-color: #c1d32f;
}
#test {
color: var(--var-txt-color);
}
</style>

I know you highlighted "without having to link any stylesheet", but I run into the same issue and there is a simple option - use just one external css file and include it in your App.vue, then you can access the variables anywhere else, in scoped styles as well.
variables.css
:root {
--font-family: "Roboto", "Helvetica", "Arial", sans-serif;
--primary-color: #333a4b;
}
App.vue
<style>
#import './assets/styles/variables.css';
</style>
LandingView.vue
<style scoped>
#landing-view {
font-family: var(--font-family);
font-weight: 300;
line-height: 1.5em;
color: var(--primary-color);
}
</style>

This won't work as expected because of scoped attribute for stylesheet. Example above compiles into:
[data-v-4cc5a608]:root {
--var-txt-color: #f00;
}
And, as you understand, it will not target actual :root element.
It can be solved by:
Not using scoped attribute for this stylesheet. Notice that it may cause styles conflict with other variables declarations for :root element.
Using current component's wrapping element as root. If we declare variables this way:
.test {
--var-txt-color: #c1d32f;
color: var(--var-txt-color);
}
.test-child-node {
background-color: var(--var-txt-color);
}
Then it will can reuse variables for other elements of the same component. But still, it won't be possible to use declared variables inside child components without removing scoped, if it is the case.

Why not just use this?
<style scoped>
* {
--var-txt-color: #c1d32f;
}
</style>
The generated CSS is:
*[data-v-d235d782] {
--var-txt-color: #c1d32f;
}
This has been working for me.
I just discovered that it looks like this also works, using the "deep selector"
>>> {
--var-txt-color: #c1d32f;
}
Generated CSS is:
[data-v-d235d782] {
--var-txt-color: #c1d32f;
}
I think I like this method more.

One workaround is to define them under a non-scoped style element like the following. However one thing to note here is, these variables will be exposed to other Vue components as well.
<style>
:root {
--var-txt-color: #c1d32f;
}
</style>
<style scoped>
#test {
color: var(--var-txt-color);
}
</style>

Late answer - Here is a working example with css vars derived from standard vue structures.
<template>
<div>
<component :is="'style'">
:root {
--color: {{ color }};
--text-decoration: {{ textDecoration }};
--font-size: {{ fontSize }};
}
</component>
<p>example</p>
</div>
</template>
<script>
export default {
props:{
color: {
type: String,
default: '#990000'
}
},
data: function () {
return {
textDecoration: 'underline'
}
},
computed: {
fontSize: function (){
return Math.round(Math.random() * (5 - 1) + 1) + 'em';
}
}
}
</script>
<style>
p{
color: var(--color);
text-decoration: var(--text-decoration);
font-size: var(--font-size);
}
</style>
Starting from the top...
Vue must have 1 root element, so I needed a div tag in order to include a sample p tag. However, you can just use the component-is-style tag and get rid of the div and p tags. Note the need for extra quotations "'style'".
The normal vue style tag can be scoped or not - as needed.

Well, now you can use CSS variable injection.
<template>
<div>
<div class="text">hello</div>
</div>
</template>
<script>
export default {
data() {
return {
color: 'red',
font: {
weight: '800'
}
}
}
}
</script>
<style>
.text {
color: v-bind(color);
font-weight: v-bind('font.weight');
}
</style>
Those styles are also both reactive and scoped. There won't be any unintended inheritance issues. Vue manages the CSS variables for you.
You can take a look at the RFC here.

Related

CSS Modules - exclude class from being transformed

I'm using CSS modules and by far everything was working great.
We started to use external UI library along with our own one, so I'm writing components like this:
<div className={styles['my-component']}>
<ExternalUIComponent />
</div>
Assuming that the ExternalUIComponent has its own class that in the final CSS file looks like this external-ui-component, how can I make adjust this component styling from my css file? The below example does not work:
.my-component {
font-size: 1em;
}
.my-component .external-ui-component {
padding: 16px;
// Some other styling adjustments here
}
Please do not use inline styles as someone else suggested. Stay away from inline styles as much as you can because they can cause unnecessary re-renders.
You should use global instead.
.my-component {
:global {
.external-ui-component {
padding: 16px;
// Some other styling adjustments here
}
}
}
https://github.com/css-modules/css-modules#usage-with-preprocessors
Also, I recommend using camel case style names which is the preferred way for css-modules.
So your class name would be : .myComponent { ... }
And you can use it in your code as
<div className={ styles.myComponent } >
If you wanted to add more styles , you can use the array.join(' ') syntax.
<div className={ [ styles.myComponent, styles.anotherStyle ].join(' ') } >
This is cleaner!
Here's a shorter form in pure CSS (i.e. no preprocessor needed):
.my-component :global .external-ui-component {
// ...
}
Did you try inline styles for that component ?
https://reactjs.org/docs/dom-elements.html#style
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}

Vue.js dynamic <style> with variables

Is it possible to add the dynamic variable in style?
I mean something like:
<style>
.class_name {
background-image({{project.background}});
}
#media all and (-webkit-min-device-pixel-ratio : 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
.class_name {
background-image({{project.background_retina}});
}
}
</style>
I faced the same problem. I have been trying to use a background color value from a database. I find out a good solution to add a background color value on inline CSS which value I set from database.
<img :src="/Imagesource.jpg" alt="" :style="{'background-color':Your_Variable_Name}">
With Vue.js 3.2 you can do State-Driven Dynamic CSS like this:
<template>
<h1 id="script">Script</h1>
<h1 id="scriptSetup">Script setup</h1>
</template>
<script>
export default {
data() {
return {
colorFromScript: 'red'
}
}
}
</script>
<script setup>
const colorFromScriptSetup = 'green'
</script>
<style>
#script {
color: v-bind('colorFromScript')
}
#scriptSetup {
color: v-bind('colorFromScriptSetup')
}
</style>
See an implementation here
The best way to include dynamic styles is to use CSS variables. To avoid inline styles while gaining the benefit (or necessity—e.g., user-defined colors within a data payload) of dynamic styling, use a <style> tag inside of the <template> (so that values can be inserted by Vue). Use a :root pseudo-class to contain the variables so that they are accessible across the CSS scope of the application.
Note that some CSS values, like url() cannot be interpolated, so they need to be complete variables.
Example (Nuxt .vue with ES6/ES2015 syntax):
<template>
<div>
<style>
:root {
--accent-color: {{ accentColor }};
--hero-image: url('{{ heroImage }}');
}
</style>
<div class="punchy">
<h1>Pow.</h1>
</div>
</div>
</template>
<script>
export default {
data() { return {
accentColor: '#f00',
heroImage: 'https://vuejs.org/images/logo.png',
}},
}
</script>
<style>
.punchy {
background-image: var(--hero-image);
border: 4px solid var(--accent-color);
display: inline-block;
width: 250px; height: 250px;
}
h1 {
color: var(--accent-color);
}
</style>
Also created an alternate more involved runnable example on Codepen.
CSS <style> is static. I don't think you can do that... you might have to look for a different approach.
You can try using CSS variables. For example, (the code below is not tested)
<template>
<div class="class_name" :style="{'--bkgImage': 'url(' + project.background + ')', '--bkgImageMobile': 'url(' + project.backgroundRetina + ')'}">
</div>
</template>
<style>
.class_name{
background-image: var(--bkgImage);
}
#media all and (-webkit-min-device-pixel-ratio : 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
.class_name {
background-image: var(--bkgImageMobile);
}
}
</style>
Note: Only the latest browsers support CSS variables.
If you still see any issues with the :style in the template then try this,
<div :style="'--bkgImage: url(' + project.background + '); --bkgImageMobile: url(' + project.backgroundRetina + ')'">
</div>
As you are using Vue.js, use Vue.js to change the background, instead of CSS:
var vm = new Vue({
el: '#vue-instance',
data: {
rows: [
{value: 'green'},
{value: 'red'},
{value: 'blue'},
],
item:""
},
methods:{
onTimeSlotClick: function(item){
console.log(item);
document.querySelector(".dynamic").style.background = item;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<div id="vue-instance">
<select class="form-control" v-model="item" v-on:change="onTimeSlotClick(item)">
<option value="">Select</option>
<option v-for="row in rows">
{{row.value}}
</option>
</select>
<div class='dynamic'>VALUE</div>
<br/><br/>
<div :style="{ background: item}">Another</div>
</div>
Yes, this is possible. Vue.js does not support style tags in templates, but you can get around this by using a component tag. Untested pseudocode:
In your template:
<component type="style" v-html="style"></component>
In your script:
props: {
color: String
}
computed: {
style() {
return `.myJSGeneratedStyle { color: ${this.color} }`;
}
}
There are lots of reasons why you shouldn't use this method. It's definitely hacky and :style="" is probably better most of the time, but for your problem with media queries I think this is a good solution.
Vue 3 State-Driven Dynamic CSS Variables
I know this is a bit late and is using Vue.js 2, but as of now in Vue.js 3 you can create state-driven CSS variables.
You can now use your SFC (Single File Component) state data inside your styles tags using v-bind().
You can read more about state-driven CSS variables here, or read the Vue.js 3 documentation here.
Here is a code example
Example
<template>
<div>
<input type="text" v-model="color" />
<div class="user-input-color">
{{ color }}
</div>
</div>
</template>
<script>
export default {
data: () => ({
color: 'white'
})
}
</script>
<style scoped>
.user-input-color {
background-color: v-bind(color)
}
</style>
Here is a link to the live example.
Links
JS Now Vue state-driven CSS variables
Vue.js 3 Docs
Live Example
You can use the component tag offered by Vue.js.
<template>
<component :is="`style`">
.cg {color: {{color}};}
</component>
<p class="cg">I am green</p> <br/>
<button #click="change">change</button>
</template>
<script>
export default {
data(){
return { color: 'green' }
},
methods: {
change() {this.color = 'red';}
}
}
</script>
I encountered the same problem and I figured out a hack which suits my needs (and maybe yours).
As <style> is contained in <head>, there is a way to make it work:
We generate the CSS content as a computed property based on the state of the page/component
computed: {
css() {
return `<style type="text/css">
.bg {
background: ${this.bg_color_string};
}</style>`
}
}
Now, we have our style as a string and the only challenge is to pass it to the browser.
I added this to my <head>
<style id="customStyle"></style>
Then I call the setInterval once the page is loaded.
mounted() {
setInterval(() => this.refreshHead(), 1000);
}
And I define the refreshHead as such:
methods: {
refreshHead() {
document.getElementById('customStyle').innerHTML = this.css
}
}
In simple terms, this is how you would do it in Vue.js and Nuxt.js:
<template>
<div>
<img :src="dynamicImageURL" alt="" :style="'background-color':backgroundColor"/>
</div>
</template>
<script>
export default{
data(){
return {
dynamicImageURL='myimage.png',
backgroundColor='red',
}
}
}
</script>
I needed to write completely dynamic styles, so I used approach beyond Vue system:
{
// Other properties.
watch: {
myProp: {
handler() {
this.styleElement.innerHTML = this.myProp.css;
},
deep: true,
},
},
mounted() {
this.styleElement = this.document.createElement('style');
this.styleElement.innerText = this.myProp.css;
this.document.head.append(this.styleElement);
},
unmounted() {
this.styleElement.remove();
},
}
Though it may have some performace issues with CSS big enough.
I liked #mickey-mullin reply, but not everything worked entirely. The url missed require, even though the information in his post helped me a lot in my case.
var(), url(), multiple ternary operators (my own case - you shouldn't need it), I was able to do so for background-image in such a way:
template
<div :style="[
case1 ? { '--iconUrl': `url(${require('../../../public/icon1.svg')})`} :
case2 ? { '--iconUrl': `url(${require('../../../public/icon2.svg')})`} :
{ '--iconUrl': `url(${require('../../../public/default.svg')})` },
]" class="myClass">
styles
div.myClass::before {
background-image: var(--iconUrl);
}
Note: I didn't have to declare iconUrl in my data() -> return.

VUE component ignoring CSS

I have the following VUE component:
<template>
<div>
<div class="bottom-footer">
{{msg}}
</div>
</div>
</template>
<script>
export default {
name: 'LayoutFooter',
data () {
return {
msg: 'my test'
}
},
mounted () {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</scoped>
VUE is completely ignoring my scoped CSS. When page is rendered its simply not applied. There are no console errors. Ive tried removing the scoped attribute and its still ignored. Any ideas why VUE is doing this?
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</style>
you need to close style

How to import stylesheet dynamically Polymer 2.0

I want to import my stylesheet with variable inside :
<dom-module id="colors-palette">
<template>
<style>
:host {
--dark-gray: #263238;
--light-gray: #e6ebed;
--light-blue: #00bcd4;
--autovision-blue: #174291;
--background-box-number-applications: red;
--color-box-number-applications: orange;
}
</style>
</template>
</dom-module>
I want to do it dynamically. My folder structure is :
-themes
-theme1
-style.html
-theme2
-style.html
-theme3
-style.html
I detect the theme when my-app is ready and after that I try to import my style like this in the ready function :
Polymer.importHref(this.resolveUrl('../themes/' + this.getCurrentTheme() + '/colors-palette.html'));
The file is loaded but the var in the style in my-app.html doesn't change :
app-header {
background-color: var(--dark-gray);
}
And I've got this error in the console :
Could not find style data in module named colors-palette
Any idea ? Or maybe I have to do otherwise ?
Thanks a lot
Your colors-palette.html should just contain the styles setting it globally for html.
<custom-style>
<style is="custom-style">
html {
--dark-gray: #263238;
--light-gray: #e6ebed;
--light-blue: #00bcd4;
--autovision-blue: #174291;
--background-box-number-applications: red;
--color-box-number-applications: orange;
}
</style>
</custom-style>

How can I use :global css classes - css modules

I'm using css modules for my reactjs project. Here is my file where I'm trying to use global css classes:
<div class='site-wrapper'></div>
<div class='site-wrapper-title'>Hello World</div>
Here is my styles.css
:global{
.site-wrapper {
color: 'green';
}
.site-wrapper-title {
color: 'red';
}
}
But it is not working. I think I'm doing mistake in importing. How can I import global css and then use it in my application?
Well, I'm newbie so sorry for foolish mistakes.
:global .site-wrapper{
color: 'green';
}
:global .site-wrapper-title {
color: 'red';
}
or
:global(.site-wrapper){
color: 'green';
}
:global(.site-wrapper-title) {
color: 'red';
}
should work for you

Resources