CSS: How to target ::slotted siblings in Shadow DOM root? - css

I know that the spec currently only allows compound selectors for ::slotted, i.e. ::slotted(my-first + my-second) is not allowed, but should something like this be working?
::slotted(x-first) + ::slotted(x-second) { /* css */ }
Is there any way to target slotted siblings (other than with global css)? And if not, where would I file such a request? Thanks.

Sure you can select siblings of slots / slotted.
The thing you can not do is select a element which has been slotted and is not a top-level node.
Select siblings:
slot[name=<slotname>] ~ <selector>
Select slotted top-level node
::slotted(<compound-selector>)
A compound-selector contains a tag/class/id/name etc. but must not have any combinators. Like <space> for example.
.myClass OK
<anyTag>[<anyAttribute>[=<anyValue>]] OK
.<myClass> > .<anotherClass> NO
Examples
var element = document.querySelector('.templateMe');
var shadow = element.attachShadow({mode: 'open'});
var template = document.querySelector('.myTemplate');
shadow.appendChild(template.content.cloneNode(true));
<template class="myTemplate">
<style type="text/css">
::slotted([slot=slot1]) { /* slot1 every slotted element - YES */
color: red;
}
slot[name=slot1] { /* slot1 itself - YES */
text-decoration: underline;
}
slot[name=slot1] + .siblingA { /* slot1 siblingA (direct sibling) - YES */
color: green;
}
slot[name=slot1] ~ .siblingB { /* slot1 siblingB (any sibling) - YES */
color: orange;
}
slot[name=slot2]::slotted(.selectMeA) { /* slot2 TOP-LEVEL CHILD (slotted) - YES */
color: purple;
}
slot[name=slot2]::slotted(.selectMeB) { /* slot2 NOT TOP-LEVEL CHILD - NO */
font-weight: bold;
}
slot[name=slot2]::slotted(.selectMeC[name=myName]) { /* slot2 TOP-LEVEL CHILD (slotted) - YES */
color: khaki;
}
slot[name=slot2] + .siblingC { /* slot2 sibling - YES */
color: blue;
}
</style>
<div>
<slot name="slot1"></slot>
<div class="siblingA">Sibling A of Slot 1</div>
<div class="siblingB">Sibling B of Slot 1</div>
</div>
<hr/>
<div>
<slot name="slot2"></slot>
<div class="siblingC">Sibling C of Slot 2</div>
</div>
</template>
<div class='templateMe'>
<span slot="slot1">Im in Solt 1</span>
<span slot="slot2" class="selectMeA">
Im in Solt 2, im selectable.
<div class='selectMeB'>
NOT selectable (because no top level node of slotted)!
</div>
</span>
<span slot="slot2" class="selectMeC" name="myName">Im in Solt 2 too and selectable!</span>
</div>
More here.
slotted elements (coming from light DOM), ::slotted(selector) allows to select slotted elements themselves, but not their children.

DOM that is placed into a slot is supposed to be controlled by the CSS that owns that DOM and not by the Custom Element.
The Web Component it allowed very minor CSS control over the DOM that is placed into your Slot. Pretty much just the top level elements (And things that are auto inherited by child nodes.)
This was a conscious decision and will probably never change.

Related

Can Referenced CSS vars be overwritten in a selector? [duplicate]

This question already has answers here:
CSS scoped custom property ignored when used to calculate variable in outer scope
(2 answers)
Closed 2 years ago.
I am trying to overwrite color value here that's not applying in Chrome at least. Wondering if this is a bug or just a limitation of CSS vars.
The --contrast-color I am overwriting isn't taking effect (#fff). However, obviously, normally overwriting --title-color works fine if I define it again in .dark as --title-color: var(--contrast-color);
:root {
--contrast-color: red;
--title-color: var(--contrast-color);
}
.dark {
--contrast-color: #fff;
background: #000;
}
.title {
color: var(--title-color);
}
<div>
<h1 class="title">
Heading (should be red)
</h1>
</div>
<div class="dark">
<h1 class="title">
Heading (should be #fff)
</h1>
</div>
the variable --title-color: var(--contrast-color); is defined for the :root element while you update --contrast-color on .dark element.
If you want to have that reference automatically updated, without the need of redeclaring --title-color, both the variables --contrast-color should be declared for the same element (e.g. on the div as in the example below, but I recommend to apply the theme class to a common container, like :root)
An update on a variable would eventually re-evaluate a reference on its descendants, not on the ancestors: in fact, if the update of the variable also affected the ancestors overriding the same property, then it would change instantly, on cascade, the other title too.
So as a result both the titles would be white coloured.
div {
--contrast-color: red;
--title-color: var(--contrast-color);
}
div.dark {
--contrast-color: #fff;
background: #000;
}
.title {
color: var(--title-color);
}
<div>
<h1 class="title">
Heading (should be red)
</h1>
</div>
<div class="dark">
<h1 class="title">
Heading (should be #fff)
</h1>
</div>

How do I style the last slotted element in a web component

I have a web component that has a template which looks like this...
<template>
<div class="jrg-app-header">
<slot name="jrg-app-header-1"></slot>
<slot name="jrg-app-header-2"></slot>
<slot name="jrg-app-header-3"></slot>
</div>
</template>
I am basically trying to set the contents of the last slot to have flex:1; in style. Is there a CSS query that will do this? I tried something list
::slotted(*):last-child{
flex:1;
}
But it did not work. How do I style the last slotted object?
For long answer on ::slotted see: ::slotted CSS selector for nested children in shadowDOM slot
From the docs: https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted
::slotted( <compound-selector-list> )
The pseudo selector goes inside the brackets: ::slotted(*:last-child)
Note: :slotted(...) takes a simple selector
See (very) long read: ::slotted CSS selector for nested children in shadowDOM slot
customElements.define('my-table', class extends HTMLElement {
constructor() {
let template = (name) => document.getElementById(name)
.content.cloneNode(true);
super()
.attachShadow({ mode: 'open' })
.append( template(this.nodeName) );
}
})
<template id="MY-TABLE">
<style>
:host { display: flex; padding:1em }
::slotted(*:first-child) { background: green }
::slotted(*:last-child) { background: yellow; flex:1 }
::slotted(*:first-of-type) { border: 2px solid red }
::slotted(*:last-of-type) { border: 2px dashed red }
</style>
<slot name="column"></slot>
</template>
<my-table>
<div slot="column">Alpha</div>
<div slot="column">Bravo</div>
<div slot="column">Charlie</div>
</my-table>
<my-table>
<div slot="column">Delta</div>
<div slot="column">Echo</div>
</my-table>
JSFiddle playground:
https://jsfiddle.net/WebComponents/108ey7b2/
More SLOT related answers can be found with StackOverflow Search: Custom Elements SLOTs

css not select the first class between other container

css doesn't select the first class
:not(:first) doesn't work because .callout is wrapped by other container
.callout:not(:first) {
color: red;
}
<div class="d-flex">
<div class="flex-fill">
<div class="callout">
Text A
</div>
</div>
<div class="flex-fill">
<div class="callout">
Text B - only this set color red
</div>
</div>
</div>
Select the .callout element whose parent is not the :first-child of its parent element
.flex-fill:not(:first-child) .callout {
color: red
}
Or just revert the logic and target the :last-child
.flex-fill:last-child .callout {
color: red
}
Or target the .callout inside the second parent element, no matter how many .flex-fill siblings you have
.flex-fill:nth-child(2) .callout {
color: red
}
Codepen example
Anyway, I don't recommend to use this kind of selectors or to rely on a specific markup structure because this approach can easily cause maintainability problems as the code grows and, if possible, I'd suggest to place instead a specific class for this purpose on the right element.

CSS Selector Within a Selector

Essentially what I am trying to do is have one element react as the hover state of a different element.
.page-template-page-services-new .imgBlock:hover { .page-template-page-services-new .ButtonService {color: #6395ce; background-color: #fff; } }
Not currently working - is this a thing? If not, how might I accomplish it. I know the selectors are correct, they work independently.
What I think you are referring to is that you've seen something akin to
.selector-one{
//style definitions
.selector-two{
//other style definitions
}
}
This comes from pre-processors such as SCSS (Sass) or LESS, I'll assume you can do a quick google on those.
For the other part of your question, yes, you can style an element differently if it's parent container or even a sibling is hovered.
Example
.container-hover:hover .red-on-hover{
background-color:red;
}
.sibling-hover:hover + .sibling-hover{
background-color:blue;
}
<div class="container-hover">
<h3>Other Text</h3>
<div class="red-on-hover">Background will turn red on hover</div>
</div>
<p class="sibling-hover"> When I am hovered, my sibling will be blue</p>
<p class="sibling-hover"> Blue? Blue</p>
For the sibling hover, please note that if you added more .sibling-hover elements that all but the first one would be able to turn blue if you hovered over it's immediately prior sibling.
It can work if they have a parent child relationship.
.page-template-page-services-new {
background: #ccc;
}
.page-template-page-services-new .imgBlock:hover .ButtonService {
color: #6395ce;
background-color: #fff;
}
<div class="page-template-page-services-new">
<div class="imgBlock">
<img src="http://placehold.it/100/100" alt="">
<div class="ButtonService">
<p>
This is a test
</p>
</div>
</div>
</div>

:not selector doesn't work as expected

So I have this:
b:not(.alert-danger) {
color: #fff;
}
<div class="alert alert-danger" role="alert">
<h3><b>text</b></h3>
</div>
<h3><b>text2</b></h3>
The thing is that the alert (text) gets white and I don't understand what's happening. text2 also gets white but that's ok.
I just want to make effect on text2, not text1.
:not(.alert-danger) > h3 {
color: #fff
}
<div class="alert alert-danger" role="alert">
<h3><b>text</b></h3>
</div>
<h3><b>text2</b></h3>
Your selector:
b:not(.alert-danger) { color: #fff; }
...is saying:
Target b elements unless they have a class of alert-danger.
This doesn't match your HTML structure. Plus you can't have a child matching on a parent, because that's not how CSS works.
The revised selector is saying:
Target h3 elements that are children of any element, unless that parent has a class of alert-danger.
You can also target the b if you prefer:
:not(.alert-danger) b { color: #fff }

Resources