Nuxt 3 with Vuetify 3 and SSR reactivity - nuxtjs3

I am trying to use Nuxt 3 together with Vuetify 3 in SSR mode. I face a problem using display's breakpoints. What is more, this functionality works with Nuxt 2 and Vuetify 2.
The code below shows only div element with red background instead of green, although the screen size is large. The reason is that the initial DOM rendering, which happens on the server side, assumes that the screen's size is small. The hydration on the client side somehow doesn't take into account, that the real size is large, although you can see in the browser's web inspector a log information result green.
<template>
<div>
<div :class="divClass">Reactivity</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { useDisplay } from 'vuetify'
const counter = ref(1)
const { lgAndUp } = useDisplay()
const divClass = computed(() => {
const result = lgAndUp.value ? 'green' : 'red'
console.log('result', result)
return result
})
</script>
<style>
.green {
background-color: green;
}
.red {
background-color: red;
}
</style>
This seems like a bug, but maybe I've done some silly mistake here. Could you look at this and verify? Thanks in advance :)
The project sources can be found on GitHub

You could use ref property and watch the lgAndUp value to update it :
<template>
<div>
<div :class="divClass">Reactivity</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { useDisplay } from 'vuetify';
const { lgAndUp } = useDisplay();
const divClass = ref('');
watch(lgAndUp, (val) => {
console.log(val);
divClass.value = val ? 'green' : 'red';
},{immediate:true});
</script>
<style>
.green {
background-color: green;
}
.red {
background-color: red;
}
</style>
DEMO

Related

Antd Drawer Style not being applied

I am a bit puzzled when following the documentation here. Everything seems to work other than the headerStyle attribute. It doesn't seem to be applying to styles that I apply to it. I'm not sure where I am going wrong.
Checking CodeSandbox from one of the examples provided headerStyle works. Where else can I check to understand what is causing this issue?
Localhost:
Codesandbox:
Not sure where your problem lies, as I'm not getting the same warning (when using headerStyle), but here's a working example -- make sure to import the ant design css, otherwise, the ant components won't work as intended. Also, I find it easier and cleaner to override ant's css via overriding their respective class names in a separate css file, rather than overriding styles -- especially when overriding more than css property.
index.js
import React, { Component } from "react";
import { render } from "react-dom";
import { Drawer, Button } from "antd";
import "antd/dist/antd.css";
import "./index.css";
class App extends Component {
state = { visible: false };
showDrawer = () => {
this.setState(prevState => ({
visible: !prevState.visible
}));
};
render() {
return (
<div>
<Button type="primary" onClick={this.showDrawer}>
Open
</Button>
<Drawer
title="Basic Drawer"
placement="right"
closable={false}
onClose={this.showDrawer}
visible={this.state.visible}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</div>
);
}
}
render(<App />, document.getElementById("container"));
index.css
.ant-drawer-header {
background-color: #5340ff;
border-radius: 0;
}
.ant-drawer-title {
color: #fff;
}
.ant-drawer-body {
background-color: #5340ff;
color: #fff;
height: calc(100vh - 55px);
}

gatsby-image: how to import the processed image into css and use it with background-image property

I sucessfully implemented the gatsby-image into my project and replace lots of img tag that are used in my components. But now im trying to optimized the background image for some of my component but i dont know how since using gatsby-image would generate a new img tag, and i cant use that to style as a background for say a div element. can s1 show me how i can use the generated images with css. Here's my code:
const HeaderlineSection = ({headerOne}) => {
return(
<div className="header-back" ></div>
)
}
export const query = graphql`
query IndexPageQuery {
headerOne: imageSharp(id: { regex: "/header_one.jpg/" }) {
sizes(maxWidth: 1200 ) {
...GatsbyImageSharpSizes
}
}
}
previously, in my css i use a non optimized image for backgroud-image:
.header-back {
background: url(../images/header_one.jpg) 50% 0 no-repeat;
height: 470px;
width: 100%;
}
I am using gatsby-background-image plugin for that. Here is one exemple of how you can use it :
import React from 'react'
import { graphql, StaticQuery } from 'gatsby'
import styled from 'styled-components'
import BackgroundImage from 'gatsby-background-image'
const BackgroundSection = ({ className }) => (
<StaticQuery query={graphql`
query {
desktop: file(relativePath: { eq: "seamless-bg-desktop.jpg" }) {
childImageSharp {
fluid(quality: 100, maxWidth: 4160) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
`}
render={data => {
const imageData = data.desktop.childImageSharp.fluid
return (
<BackgroundImage Tag="section"
className={className}
fluid={imageData}
backgroundColor={`#040e18`}
>
<h1>Hello gatsby-background-image</h1>
</BackgroundImage>
)
}
}
/>
)
const StyledBackgroundSection = styled(BackgroundSection)`
width: 100%;
background-repeat: repeat-y;
`
export default StyledBackgroundSection
The code is self-explanatory, but basically, element will be replaced with the element that you select in the Tag attribute and will have the background image set to the one selected with the graphql imageSharp query.

Vue.js - cannot add click event

I'm trying to add click event listener for DOM. All the scripts are bundled with webpack which compiles it to 1 large file.
This is my component:
<template>
<div class="formchat-window">
<a>test</a>
<div class="title" v-on:click.self="toggle">Nevíte si rady? Zeptejte se!</div>
<wp-formchat-screen v-if="windowActive"></wp-formchat-screen>
</div>
</template>
<script>
export default {
mounted () {
console.log('mounted');
},
methods: {
toggle(event) {
alert('click on toggle');
this.windowActive = !this.windowActive;
}
},
data() {
return {
windowActive: false,
};
}
}
</script>
<style lang="scss">
$color: red;
.formchat-window {
position: fixed;
right: 10%;
bottom: 0;
width: 300px;
z-index: 9999;
.title {
background: $color;
}
}
</style>
This is my main JS file:
import Vue from 'vue'
import FormChat from './FormChat'
import FormchatAnswer from './components/FormchatAnswer'
import FormchatEntry from './components/FormchatEntry'
import FormchatScreen from './components/FormchatScreen'
import FormchatWindow from './components/FormchatWindow'
window.Vue = Vue;
console.log('test');
Vue.component('WpFormchatAnswer', FormchatAnswer);
Vue.component('WpFormchatEntry', FormchatEntry);
Vue.component('WpFormchatScreen', FormchatScreen);
Vue.component('WpFormchatWindow', FormchatWindow);
const app = new Vue({
el: '#wp-formchat-vue-root',
//render: h => h(FormChat)
});
However I did everything and compiler works perfectly, I cant register the click, so the method won't trigger.
Can anyone please help me?
I have finally figured it out. In the console, there was a warning about component name and main filename. I have renamed main file and the Vue started to work again.
Beware of ambigious naming :)

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.

In vue.js component, how to use props in css?

I'm new to vue.js. Here is my problem:
In a *.vue file like this:
<template>
<div id="a">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color']
}
</script>
<style scoped>
#a {
background-color: ?
}
<style>
How can I use the props color in background-color: (where is a ? now).
Thanks.
You actually can!
You should define the CSS variables in a Computed Property, then call the computed property as a style attribute to the element that will require the CSS variable, and finally you may use the variable within the tags at the bottom of your document.
new Vue({
el: '#app',
data: function() {
return {
baseFontSize: 1,
bgHoverColor: "#00cc00",
hoverContent: "Hovering!"
}
},
computed: {
cssProps() {
return {
'--hover-font-size': (this.baseFontSize * 2) + "em",
'--bg-hover-color': this.bgHoverColor,
'--hover-content': JSON.stringify(this.hoverContent)
}
}
}
})
div {
margin: 1em;
}
div.test:hover {
background-color: var(--bg-hover-color);
font-size: var(--hover-font-size);
}
div.test:hover::after {
margin-left: 1em;
content: var(--hover-content);
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app" :style="cssProps">
<div>Hover text: <input type="text" v-model="hoverContent"></div>
<div>Hover color: <input type="color" v-model="bgHoverColor"></div>
<div class="test">Hover over me</div>
</div>
Or have a look here: https://codepen.io/richardtallent/pen/yvpERW/
And here: https://github.com/vuejs/vue/issues/7346
You don't. You use a computed property and there you use the prop to return the style of the div, like this:
<template>
<div id="a" :style="style" #mouseover="mouseOver()">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color'],
computed: {
style () {
return 'background-color: ' + this.hovering ? this.color: 'red';
}
},
data () {
return {
hovering: false
}
},
methods: {
mouseOver () {
this.hovering = !this.hovering
}
}
}
</script>
<style scoped>
<style>
As we are in 2020 now, I suggest using this trick with a css function called var
<template>
<div id="a" :style="cssVars"></div>
</template>
<script>
export default {
props: ['color'],
computed: {
cssVars () {
return{
/* variables you want to pass to css */
'--color': this.color,
}
}
}
<script>
<style scoped>
#a{
background-color: var(--color);
}
</style>
This method is very useful because it allows you to update the passed values through css later on (for example when you apply hover event).
credit
I know we're talking vue 2 here, but in case anyone from vue 3 lands in this question (like I did), vue 3 introduced a much cleaner way to do this:
<template>
<div id="a">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color']
}
</script>
<style scoped>
#a {
background-color: v-bind(color);
}
<style>
What Vue actually does behind the scenes is the same "introducing css variables through component's style process", but it sure looks much better on the eyes now.
Documentation source: https://v3.vuejs.org/api/sfc-style.html#state-driven-dynamic-css
Why not just use :style prop in this way:
<template>
<div :style="{ backgroundColor: color }">
</template>
<script>
export default {
props: {
color: {
type: String,
default: ''
}
}
}
</script>
Make sure you define css properties in camelCase style.
If you need css that can't be applied by a style attribute like pseudo classes or media queries, what I do is the following:
Create a globally available style component when initializing Vue (you need it as otherwise you run into linting issues). It creates a style tag that simply renders the content in the slot:
I would only use this if you really need both dynamic values in your css and css features that can't be applied to a style attribute.
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
Vue.component('v-style', {
render: function(createElement) {
return createElement('style', this.$slots.default)
}
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Then use it at the top of your template like this and you get the full JavaScript scope of your component and the full css syntax combined:
<template>
<v-style>
#media screen and (max-width: 820px) {
.gwi-text-media-{{ this.id }} {
background-image: url({{ mobileThumb }});
}
}
</v-style>
</template>
It seems a bit hacky to me, but it does it's job and I would rather go like this in some cases than having to add additional JS for mouse-over or resize events that have a big potential to slow down your application performance.
Vue 3 added new way of binding styles, so now you can easily bind your props to css properties.
Read source:
https://learnvue.co/2021/05/how-to-use-vue-css-variables-reactive-styles-rfc/
<template>
<div>
<div class="text">hello</div>
</div>
</template>
<script>
export default {
data() {
return {
color: 'red',
}
}
}
</script>
<style>
.text {
color: v-bind(color);
}
</style>
You could utilise the CSS var(--foo-bar) function. It is also useful if you are trying to pass an asset that has its own dynamic path, like Shopify does.
This method also works for styling the :before and :after elements as they refer back to the style applied on the owner element.
Using the original post example for passing a colour:
<template>
<div
id="a"
:style="{ '--colour': color }">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color']
}
</script>
<style scoped>
#a {
background-color: var(--colour);
}
</style>
Using the original post example for passing an URL:
<template>
<div
id="a"
:style="{ '--image-url': 'url(' + image + ')' }">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['image']
}
</script>
<style scoped>
#a {
background-url: var(--image-url);
}
</style>
Source

Resources