How do I add a class to a slotted element? - stenciljs

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>

Related

Vuetify - Overriding template slots with scoped CSS

I got some problems with overriding the CSS on slots inside an autocomplete.
I read this thread and tried multiple solutions, but none work that are feasible (since they globally change the style for vuetify components):
How to override vuetify styles?
How would I override the autocomplete styles? E.g right now my problem is that I'm adding an append-slot with a button inside the search field, but the padding of the text field pushes it too much to the left & no padding is applied on the bottom.
Some things I tried:
Creating a parent element with an ID and then manually trying to create a class for it.
Example:
#handlesearch > div > div > div.v-input__slot > div.v-select__slot > div {
margin-top: 4px !important;
}
<template id="handlesearch" slot="append">
<v-btn
>Sök</v-btn
>
</template>
If you inspect your html, you'll notice that adding ID on <template> is not working. If you move your id="handlesearch" to actual html element inside your slot, which is in this case v-btn.
<template slot="append">
<v-btn id="handlesearch">Sök</v-btn>
</template>
with next scoped style
<style scoped>
#handlesearch {
background: red;
}
</style>
Produces next result:
If I move id="handlesearch" to <template> as <template id="handlesearch"> style will not be applied since in my DOM there is no HTML element with that id.
Solution
Move your ID to actual element inside your slot, and add scoped style according to that.

Vue scoped styling not working with elements that have been added inside a slot

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.

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>

How to provide dynamic styling to riotjs tag

I want to create a riotjs tag to whom some style or css class can be added directly in HTML page. And I also can set some style attributes dynamically from tag script;
For Example;
<my-tag class="some class" attr1="" attr2="" />
<my-tag>
<script>
if(opts.attr1) //set some style to my-tag
</script>
</my-tag>
I can achieve it by creating a HTML tag string with dynamic properties and insert as HTML to root. But I don't want to add additional child tag just for styling.
What about something like this. You pass some options to the tag, and then change the style classes dynamically. Check the example http://plnkr.co/edit/ZuPMFFBIuDPSeawsEkPX?p=preview
<my-tag color="green"></my-tag>
<my-tag color="red"></my-tag>
<my-tag>
<h1 class="{opts.color}">{ message }</h1>
<script>
this.message = 'hello there'
</script>
<style>
.red {
color: red;
}
.green {
color: green;
}
</style>
</my-tag>
You can optionally include classes onto your elements by using the syntax below.
<my-tag>
<div class="some-class another-class { my-class: attr1 }"></div>
<script>
this.attr1 = this.opts.attr1;
</script>
</my-tag>
And then in the HTML where you use the tag.
<my-tag attr1="whatever"></my-tag>
So if this.opts.attr1 is set, this will add the class my-class to the <div> element.