NuxtJS: styles of inline SVG not exported to resulting CSS - vue.js

I want to style inline SVG, but NuxtJS (or better say WebPack) doesn't include this styling into output CSS:
<template>
<header>
<NuxtLink to="/" v-html="logoIco" class="logo"></NuxtLink>
</header>
</template>
<script>
export default {
name: 'HeaderComponent',
computed: {
logoIco() {
return require(`assets/images/logo.svg?raw`);
},
},
};
</script>
<style scoped lang="scss">
header {
.logo {
background: red; // this style included in result CSS
svg {
background: yellow; // and this NOT
}
}
}
</style>
WebPack doesn't see any SVG during build and doesn't include header .logo svg rule in resulting CSS.
How this can be done?

I've found an idea here: https://github.com/nuxt-community/svg-module/issues/72
This code works as expected:
<template>
<header>
<NuxtLink to="/" v-html="logoIco" class="logo">
<component :is="require(`assets/images/logo.svg?inline`)"></component>
</NuxtLink>
</header>
</template>
<script>
export default {
name: 'HeaderComponent',
};
</script>
<style scoped lang="scss">
header {
.logo {
background: red; // this style included in result CSS
svg {
background: yellow; // and this now also included
}
}
}
</style>

Related

Is it possible to create a v-class Vue directive that can be overridden by parent just like regular :class prop?

Please see this minimum example
I have a Child.vue and Parent.vue which look like this
<!-- Child.vue -->
<template>
<div :class="'bg-gray text-white'">I'm Child</div>
</template>
<template>
<Child class="p-4" />
</template>
<script>
import Child from "./Child.vue";
export default {
components: {
Child,
},
};
</script>
<style>
.p-4 {
padding: 16px;
}
.bg-gray {
background: gray;
}
.text-white {
color: white;
}
</style>
The final class name for Child.vue wrapper element will be bg-gray text-white p-4, class names are all merged.
However, I would like to create a v-class directive and make my API looks like this
<!-- Child.vue -->
<template>
<div v-class="'bg-gray text-white'">I'm Child</div>
</template>
<script>
export default {
directives: {
class: {
inserted(el, binding) {
const css = String.raw; // Not doing anything right now.
el.className = css`
${binding.value}
`;
},
},
},
};
</script>
And Parent.vue stays the same.
Now the final merged class name is bg-gray text-white, p-4 is missing!
How can I preserve p-4 just like I'm using regular :class prop?
My use case is I want to use the tagged template literal function in my Vue template.
However, Vue doesn't support template literal function in the template, the compiler will yell!
For example
<div :class="css`text`"></div>
Try out to push the value to the class list :
el.classList.add( css`
${binding.value}
`);

How to add Phylotree.js to nuxt?

I installed phylotreejs using the following command:
npm install --save phylotree
When I try to import this into a page like this:
import * as d3 from 'd3';
import phylotree from 'phylotree/build/phylotree';
I get the following error:
Cannot read property 'phylotree' of undefined
It would be really helpful if I can understand what's going on here.
I read and tried the following things by reading nuxt documentation [https://nuxtjs.org/faq/]
import phylotree from '~/node_modules/phylotree/build/phylotree.js'
export default {
head () {
return {
script: [
{ src: '~/node_modules/phylotree/build/phylotree.js' }
],
}
},
}
but nothing seems to work
Following is the Vue page, I'm trying to create:
<template>
<div>
<section class="section">
<div class="container">
<div id="phylotree"></div>
</div>
</section>
</div>
</template>
<script>
import * as d3 from 'd3';
import phylotree from 'phylotree';
import _ from 'lodash';
export default {
data:() => ({
example_tree : "(((EELA:0.150276,CONGERA:0.213019):0.230956,(EELB:0.263487,CONGERB:0.202633):0.246917):0.094785,((CAVEFISH:0.451027,(GOLDFISH:0.340495,ZEBRAFISH:0.390163):0.220565):0.067778,((((((NSAM:0.008113,NARG:0.014065):0.052991,SPUN:0.061003,(SMIC:0.027806,SDIA:0.015298,SXAN:0.046873):0.046977):0.009822,(NAUR:0.081298,(SSPI:0.023876,STIE:0.013652):0.058179):0.091775):0.073346,(MVIO:0.012271,MBER:0.039798):0.178835):0.147992,((BFNKILLIFISH:0.317455,(ONIL:0.029217,XCAU:0.084388):0.201166):0.055908,THORNYHEAD:0.252481):0.061905):0.157214,LAMPFISH:0.717196,((SCABBARDA:0.189684,SCABBARDB:0.362015):0.282263,((VIPERFISH:0.318217,BLACKDRAGON:0.109912):0.123642,LOOSEJAW:0.397100):0.287152):0.140663):0.206729):0.222485,(COELACANTH:0.558103,((CLAWEDFROG:0.441842,SALAMANDER:0.299607):0.135307,((CHAMELEON:0.771665,((PIGEON:0.150909,CHICKEN:0.172733):0.082163,ZEBRAFINCH:0.099172):0.272338):0.014055,((BOVINE:0.167569,DOLPHIN:0.157450):0.104783,ELEPHANT:0.166557):0.367205):0.050892):0.114731):0.295021)",
}),
mounted (){
// const phylotree = require('phylotree');
var tree = d3.layout.phylotree()
.svg(d3.select("#phylotree"));
tree(example_tree).layout();
}
};
</script>
<style scoped>
#spaced {
letter-spacing: 3px;
}
.section {
padding: 1.5rem 1.5rem;
}
</style>
I was able to figure out what was going on. The d3 version for phylotree.js is v3 while I was using v5. But even then it didn't work. So, I tried it in a simple html file and found out the requirements as in this page Link.
Then tried the following script, it worked. But is there a better way to do this. Can anyone help me figure this out!
<template>
<section class="section">
<div class="container">
<svg id="tree_display" />
</div>
</section>
</template>
<script>
export default {
head () {
return {
script: [
{ src: 'https://d3js.org/d3.v3.min.js' },
{ src: 'https://veg.github.io/phylotree.js/phylotree.js' },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js' },
],
}
},
mounted (){
var example_tree = "(((EELA:0.150276,CONGERA:0.213019):0.230956,(EELB:0.263487,CONGERB:0.202633):0.246917):0.094785,((CAVEFISH:0.451027,(GOLDFISH:0.340495,ZEBRAFISH:0.390163):0.220565):0.067778,((((((NSAM:0.008113,NARG:0.014065):0.052991,SPUN:0.061003,(SMIC:0.027806,SDIA:0.015298,SXAN:0.046873):0.046977):0.009822,(NAUR:0.081298,(SSPI:0.023876,STIE:0.013652):0.058179):0.091775):0.073346,(MVIO:0.012271,MBER:0.039798):0.178835):0.147992,((BFNKILLIFISH:0.317455,(ONIL:0.029217,XCAU:0.084388):0.201166):0.055908,THORNYHEAD:0.252481):0.061905):0.157214,LAMPFISH:0.717196,((SCABBARDA:0.189684,SCABBARDB:0.362015):0.282263,((VIPERFISH:0.318217,BLACKDRAGON:0.109912):0.123642,LOOSEJAW:0.397100):0.287152):0.140663):0.206729):0.222485,(COELACANTH:0.558103,((CLAWEDFROG:0.441842,SALAMANDER:0.299607):0.135307,((CHAMELEON:0.771665,((PIGEON:0.150909,CHICKEN:0.172733):0.082163,ZEBRAFINCH:0.099172):0.272338):0.014055,((BOVINE:0.167569,DOLPHIN:0.157450):0.104783,ELEPHANT:0.166557):0.367205):0.050892):0.114731):0.295021)";
var tree = d3.layout.phylotree()
.svg(d3.select("#tree_display"));
tree(example_tree).layout();
}
};
</script>
<style scoped>
#spaced {
letter-spacing: 3px;
}
.section {
padding: 1.5rem 1.5rem;
}
</style>

Component incorrectly outputting content from other component

I am using Gridsome for the first time. I am just trying to setup the layout of my website - I don't have any API connected or anything, just trying to output raw HTML. I have a SkipToContent component and a Header component. Both are outputting the contents of the Header component, and I'm rather confused. I've tried restarting the local server and general debugging stuff like making sure the files are all saved. Below are the files:
Header.vue
<template>
<header class="header">
<strong>
<g-link to="/">{{ $static.metadata.siteName }}</g-link>
</strong>
<nav class="nav">
<g-link class="nav__link" to="/">Home</g-link>
<g-link class="nav__link" to="/about/">About</g-link>
<g-link class="nav__link" to="/web/">Web Development</g-link>
<g-link class="nav__link" to="/photography/">Photography</g-link>
</nav>
</header>
</template>
<script lang="ts">
import Vue from "vue";
export default class Header extends Vue {}
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
</style>
SkipToContent.vue
<template>
Skip to content
</template>
<script lang="ts">
import Vue from 'vue';
export default class SkipToContent extends Vue {}
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
.skip-to-content {
&__link {
position: fixed;
left: 50%;
transform: translate(-50%, -200%);
transition: transform 0.15s ease;
background: $primary;
&:focus {
transform: translate(-50%, 50%);
}
}
}
</style>
Default.vue
<template>
<div class="layout">
<SkipToContent />
<Header />
<main id="main-content">
<slot />
</main>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import SkipToContent from "~/components/SkipToContent.vue";
import Header from "~/components/Header.vue";
import { Component } from "vue-property-decorator";
#Component({
components: {
Header,
SkipToContent,
}
})
export default class App extends Vue {
};
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
#import url("https://rsms.me/inter/inter.css");
html {
font-family: "Inter", sans-serif;
}
#supports (font-variation-settings: normal) {
html {
font-family: "Inter var", sans-serif;
}
}
</style>
HTML Output:
I feel like I'm either doing something very obvious incorrectly, or this is some sort of issue with using TypeScript in Gridsome?
Thanks for any suggestions :)

:hover color in vue components from props

Some of my single-file components need to take hover color from props.
My solution is that i set css variables in the following way (the main part is in the mounted(){...})
<template>
<div class="btnWrapper" ref="btnWrapper">...</div>
</template>
...
...
props() {
color1: {type: String, default: 'blue'},
},
mounted () {
this.$refs.btnWrapper.style.setProperty('--wrapHoverColor', this.color1)
}
...
...
<style scoped>
.btnWrapper {
--wrapHoverColor: pink;
}
.btnWrapper:hover {
background-color: var(--wrapHoverColor) !important;
}
</style>
This solution seems kind of woowoo.
But maybe there is no better way with pseudo elements, which are hard to control from js.
Do you guys ever take pseudo element's properties from props in vue components?
You have two different ways to do this.
1 - CSS Variables
As you already know, you can create CSS variables from what you want to port from JS to CSS and put them to your root element :style attr on your components created hooks, and then use them inside your CSS codes with var(--x).
<template>
<button :style="style"> Button </button>
</template>
<script>
export default {
props: ['color', 'hovercolor'],
data() {
return {
style: {
'--color': this.color,
'--hovercolor': this.hovercolor,
},
};
}
}
</script>
<style scoped>
button {
background: var(--color);
}
button:hover {
background: var(--hovercolor);
}
</style>
2 - Vue Component Style
vue-component-style is a tiny (~1kb gzipped) mixin to do this internally. When you active that mixin, you can write your entire style section inside of your component object with full access to the component context.
<template>
<button class="$style.button"> Button </button>
</template>
<script>
export default {
props: ['color', 'hovercolor'],
style({ className }) {
return [
className('button', {
background: this.color,
'&:hover': {
background: this.hovercolor,
},
});
];
}
}
</script>

Changing styles only when a Vue component is rendered

I'm currently using vue-router to send us to an about page like so:
# #/views/About
<template>
<p>About Vue...</p>
</template>
<script>
[...]
</script>
<style>
</style>
The way routes are rendered are like so:
# #/App.vue
<template>
<Header />
<router-view>
</template>
<script>
import ...
</script>
<style>
header {
background: black;
}
</style>
Question:
How would I go about changing the header background color ONLY when I am on the About.vue (if i go to other routes like / or /contact header should stay the same)?
Thank you in advance for the help
Use a watcher or a computed property to watch your this.$route then conditionally apply a class or style based on if you're currently on the /about route. For example ..
# #/App.vue
<template>
<Header :class="[altBackground ? 'header-red' : 'header-black']" />
<router-view>
</template>
<script>
import ...
computed: {
altBackground() {
const path = this.$route.path.split('/')
return path[path.length-1].toLowerCase() === 'about'
}
}
</script>
<style>
.header-black {
color: black;
}
.header-red {
color: red;
}
</style>