Vue scoped styling not working with elements that have been added inside a slot - vue.js

Moving my styling to a locale setup but seeing that when I insert element into slot containers they will not work. I guess this is normal if so is there a way to solve this, keep in mind NO I am not going to place the parent (.child) styling in the parent component and NO I want to keep my CSS in seperate scss files.
component (parent)
<template>
<wrapper>
<div class="child">Hello</div>
</wrapper>
</template>
component (wrapper called)
<template>
<div class="wrapper">
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
#import "../../../sass/components/_container.scss";
</style>
container.scss
.wrapper{
background-color:#333;
.child{
background-color:#fff;// not doing anything
}
}

You can use the v-deep combinator to target child elements/components e.g.:
.wrapper::v-deep .child { background-color:#fff; }
See the Deep Selectors documentation for more detail.
Update
It seems ::v-deep .child has been deprecated. Use ::v-deep(.child) {} or :deep(.child) {} instead. See the RFC for more detail.

Related

How do I add a class to a slotted element?

In my my-dropdown component in Stencil, I have this:
<slot name="filter" />
In my HTML, I have this:
<my-dropdown label="dropdown" size="m" filter="true">
<select slot="filter" class="inf__dropdown-select">
<option>One</option>
<option>Two</option>
<option>Three</option>
</select>
</my-dropdown>
The class inf__dropdown-select is defined in the stylesheet of the component, and it used to be applied to the select element inside the component.tsx, but since I need to slot it now, I replaced the select element with a single <slot name="filter" /> slot, but now I don't know how do I apply the class? If I add the class to the slot inside the component, it's not being
applied. I thought adding it to the element that you are slotting would work, but it doesn't. How do I make sure the class is applied to the slotted select element?
You have implemented the inf__dropdown-select style class inside shadow DOM (the style for my-dropdown), but the class is being set on the select element which is in light DOM, so the style can never be applied to the element.
To style elements inside a slot of a web component from inside the component, you use the ::slotted() pseudo-element selector inside the component's stylesheet.
For example - the stylesheet for my-dropdown:
:host {
::slotted(select) {
...
}
...
}
You can also use other selectors inside the ::slotted() function such as ::slotted(.inf__dropdown-select), ::slotted(select.inf__dropdown-select) or ::slotted([slot="filter"]) depending on how specific you need to be according to the design of your component.
Important to note: Slotted content is reflected, NOT moved!
<slot> content is styled by CSS in the container
in the SO snippet below global CSS myClass
Below <style> can be inside the component (in lightDOM !!) or outside the component.
and inside the component,
the "skin" (only the first! element select) can be styled with ::slotted
For really deep dive, see: ::slotted CSS selector for nested children in shadowDOM slot
customElements.define("my-dropdown",class extends HTMLElement{
constructor(){
super()
.attachShadow({mode:"open"})
.innerHTML = `
<style>
::slotted(select) {
margin:3em;
}
div {
background:pink;
}
</style>
<div>
<slot name="filter">NO slot named: filter</slot>
</div>
`
}
})
<style>
select {
font:20px arial;
}
</style>
<my-dropdown label="dropdown" size="m" filter="true">
<style>
.myClass {
border: 5px dashed green;
}
</style>
<select slot="filter" class="myClass">
<option>One</option>
<option>Two</option>
<option>Three</option>
</select>
</my-dropdown>

Vue style scoped

<template>
<div>
<test />
<test1 />
<test2 />
</div>
</template>
<style scoped>
#import '#/main.css'
</style>
I know scoped will not let parent's style leak to child components.
But if I want the import style leak to all child components, and not let it leak to global.
This style is used for all child components, but not for global styles.
The way I found is import style in every child components, but if there are too much child components, I need too import style in every child components.
Any way to do that?
This is my codesanbox example.
If you want a selector in scoped styles to be "deep", i.e. affecting child components, you can use the >>> combinator:
Parent component:
<template>
<div class="parent">
<test1/>
<test2/>
</div>
</template>
<style scoped>
.parent >>> .child {
// ...
}
</style>
Child component:
<template>
<div class="child"></div>
</template>
The above will be compiled into:
<div class="parent" data-v-f3f3eg9>
<div class="child"></div>
<div class="child"></div>
</div>
.parent[data-v-f3f3eg9] .child {}
More tips on how to use deep can be found here

Vuetife overwrite components scoped styles

I create an application using Vuetify. One component should display a schedule that is created using dxhtmlScheduler. But global Vuetify styles overwrite local dxhtmlScheduler styles and therefore the schedule is not displayed correctly. How to make dxhtmlScheduler styles a particular component had a higher priority than the styles Vuetify?
<template >
<div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'>
<div class="dhx_cal_navline">
<div class="dhx_cal_prev_button"> </div>
<div class="dhx_cal_next_button"> </div>
<div class="dhx_cal_today_button"></div>
<div class="dhx_cal_date"></div>
<div class="dhx_cal_tab" name="day_tab"></div>
<div class="dhx_cal_tab" name="week_tab"></div>
<div class="dhx_cal_tab" name="month_tab"></div>
</div>
<div class="dhx_cal_header"></div>
<div class="dhx_cal_data"></div>
</template>
<script>
import 'dhtmlx-scheduler'
export default {
name: "Scheduler",
mounted() {
scheduler.init("scheduler_here");
}
}
</script>
<style scoped>
#import "../../node_modules/dhtmlx-scheduler/codebase/dhtmlxscheduler_material.css";
</style>
The markup generated by dhtmlxScheduler lives outside vue component scope, so I don't think scoped styles would work with it.
Try removing scoped attribute from your style declaration
<style>
#import "../../node_modules/dhtmlx-scheduler/codebase/dhtmlxscheduler_material.css";
</style>
Here is a related github issue https://github.com/vuejs/vue-loader/issues/559
Explanation:
Vue adds a special data-v-xxxxxxx attribute to html elements created by your component.
Vue css-loader in its turn modifies selectors in scoped css in order to explicitly match elements inside your component and nothing outside.
So .dhx_cal_event_line { background: blue; } in dhtmlscheduler.css becomes .dhx_cal_event_line[data-v-xxxxxxx] { backtround: blue; } when loaded to the page. Such style is supposed to only affect a component with the matching value of data-v attribute.
The problem is, DOM elements of dhtmlxscheduler are created by the library code outside the vue component knowledge, they don't have vue data-v-xxxxxxx attributes, thus scoped css selectors no longer matches them.
Thus, you'll have to load scheduler css as a global style.
There is maybe a better solution, but unfortunately, I don't know it.

Vue: What is the cleanest way to select a component via CSS?

I have a bar component. It is used like this:
<template>
<div>
<!-- stuff -->
<bar></bar>
<!-- stuff -->
<!-- stuff -->
<!-- stuff -->
<bar></bar>
<!-- stuff -->
<bar></bar>
<!-- stuff -->
</div>
</template>
<style lang="scss" scoped>
#media (max-width: 1300px) {
// this selector doesn't work, but it would be nice if it did
bar {
display: none;
}
}
</style>
I would like to hide the bar elements when the screen is 1300px or narrower. It would be nice if there was a bar element selector, just like there are p and h1 element selectors. However, there doesn't seem to be, and I have to add class="bar" in order to select them.
My question is if there is a cleaner way to select the bar elements.
It wouldn't be good to add the CSS code inside of the bar component because when the bars are used inside of other components, I don't want to hide them at all.
A class seems to be the best way to go. Put it on the root element of the component if you want it to be universal for the component, or only on the component tag if you want it to be specific to that use.
Also, there is no reason you couldn't use a custom tag as the root element of your component; as long as the tag didn't map to a component, it would be left in the DOM, and you could use it for CSS selection. I don't recommend it, though, as I don't think this use case is a good reason for introducing a new tag.
If your component template looked like this, for example:
<template>
<bar-container>
Hi there
</bar-container>
</template>
and you had no bar-container component defined, you would be able to use CSS to select bar-container, which would be the container element for every bar component. But it's just as easy to use <div class="bar-container"> instead.
After better understanding the problem at hand, would this work?
<div class="someClass">
<bar v-bind:width="draw.probability" type="draw" ref="myComponent"></bar>
</div>

v-if on a component template tag

From the docs:
Because v-if is a directive, it has to be attached to a single
element. But what if we want to toggle more than one element? In this
case we can use v-if on a element, which serves as an
invisible wrapper. The final rendered result will not include the
element.
But on my template in my component:
<template v-if="false">
<div>
....
</div>
</template>
But the component still renders.
I ask because I want a hook on the component so if v-if is true, I can do some code in beforeMounted and beforeDestroyed if false.
If I undestood what are you doing...
You're putting v-if int the template tag ina .vue file right?
Like this
// component.vue
<template v-if="false">
<div>
My Component
</div>
</template>
<script>
export default {
name: 'my-component'
};
</script>
<styles>
</styles>
Right?
If YES, you are doing it wrong.
The template there is a tag for Webpack Vue Loader to load the component template.
So the if must go inside the template tag.
// component.vue
<template>
<div v-if="false">
My Component
</div>
</template>
<script>
export default {
name: 'my-component'
};
</script>
<styles>
</styles>
If you need to "hide" multiple elements, just encapsulate into another div.
As Lucas Katayama said, you cannot use v-if inside SFC, but another way to hide you component is use v-if on this component in its parent component.
Your reference to the docs is correct, you can use a v-if on a template tag. However, I believe conditionals on the top-level <template> in a Single File Component are ignored.
To achieve the effect showed in the docs (conditional render a template) that template needs to be within the top-level template section.
Example:
<script>
// your script section
</script>
<template>
<template v-if="false">
<div>
....
</div>
</template>
</template>
<style>
// your style section
</style>