Style children of shadow dom ::part element - css

I have a web component that renders the following in the shadow dom:
<custom-banner>
#shadow-root
<div part="headertext">
I am the header text!
</div>
...
</custom-banner>
To style the headertext, the following css works great:
custom-banner::part(headertext) {
border: 5px solid green;
}
Now say I have something like this:
<custom-banner>
#shadow-root
<div part="headertext">
I am the header text!
<span>I am the subheader text!</span>
</div>
...
</custom-banner>
Is there a way to target the children of a shadow part? That is, something like this (which doesn't seem to work):
custom-banner::part(headertext) span {
border: 5px solid red;
}
I realize that this sort of thing might undercut the whole purpose of ::part, but maybe not?
To be clear, the subheader span is not a slotted child in this example. It is always part of the component and it is in the shadow dom. The examples above are meant to be the rendered component, in browser.
Thanks!

Alas, you can only style the ::part Node itself.
Not children, that would defeat the ::part purpose,
might as well allow all shadowDOM styling from the outside then. (can't be done)
You can specify a part="subheader",
or you could use a CSS property, scoped to the part, see --subheader: blue
good blogs:
Why is my shadowDOM inheriting styles
::parts by Monica Dinculescu (Google): https://meowni.ca/posts/part-theme-explainer/
<style>
body {
/* note how 'inheritable styles' do style shadowDOM */
font: 28px Arial;
color: green;
}
custom-banner::part(headertext) {
/* style shadowDOM from global CSS */
background: pink;
--subheader: blue;
}
</style>
<custom-banner></custom-banner>
<script>
customElements.define("custom-banner", class extends HTMLElement {
constructor() {
super()
.attachShadow({mode:"open"})
.innerHTML = `<style> span { color:var(--subheader) } </style>` +
`<div part="headertext">I am the header text!` +
`<span>I am the subheader text!</span>` +
`</div>`;
}
});
</script>

Related

CSS :host vs CSS Class in Web Components

With a normal React/Vue component I normally create a custom CSS class and use that to style the component like so:
<div class="foo">
...
</div>
.foo {
}
With web components, there is the additional option to use the :host psuedo-selector. I understand that it can be used to apply styles to the web component HTML element itself.
<div class="foo">
...
</div>
.foo {
}
:host {
}
When should I use :host versus a custom CSS class like .foo?
An important part you do not mention, is that React (and other Frameworks), in the build step, rewrites all your classNames, to create "unique" class names.
React does not scope CSS like shadowDOM does.
Below answer is for a Web Component with shadowDOM;
Note that shadowDOM can be attached to regular DOM Elements as well. Same answer applies.
:host refers to your ... host...Web Component DOM element: <my-element>
Bluntly said, you could compare it to html,head,body in global CSS, it is the container element
The CSS inside does not (have to) know the element name my-element
classes (or any CSS selector you know) style Web Component content
OR, if you are not using shadowDOM, they style your whole document,
because unlike Frameworks, classNames are not changed, to be "unique", in the Build step.
And do learn <slot> behaviour:
::slotted CSS selector for nested children in shadowDOM slot
<style>
.foo {
border:1px solid red; /* CSS not applied to elements in shadowDOM */
font: 30px Arial; /* for UX consistancy, font styles DO style shadowDOM */
}
</style>
<span class="foo">
<my-element>Hello</my-element>
Web Components
<my-element pink>World</my-element>
</span>
<template id="MY-ELEMENT">
<style>
:host {
display:inline-block;
background:lightgreen;
}
:host([pink]) { background:hotpink }
.foo { font-weight:bold; /* does NOT style anything outside this shadowDOM */ }
</style>
<slot class="foo"></slot>
</template>
<script>
customElements.define('my-element', class extends HTMLElement {
constructor() {
super().attachShadow({mode: 'open'})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
});
</script>

How to maintain the existing background color of h3 element when hover on selected item

I have an accordion like control in which an item will be expanded and another will be collapsed. I have a common background color (which differs for various themes ) and a different hover color for all headers.
I need to maintain the background color for active item which is expanded and hover color need not to be applied for this item alone.
I have a class to identify this and I apply a certain background through hover selector
CSS
.e-active:hover {
background: #f00;
}
I tried with transparent and none but it changes the background to white
This is hard coded CSS but I need a generic CSS such that the existing background color will be maintained for active h3 element on hover state for any theme
You can use the :not css selector
div {
background: green;
padding: 2px 20px;
color: #ffffff;
}
h3 {
background: blue;
padding: 6px;
}
h3:not(.e-active):hover {
background: red;
}
<div>
<h3 class="e-active">Heading 1</h3>
<h3>Heading 2</h3>
<h3>Heading 3</h3>
</div>
You're probably going to need to include some form of JavaScript/jQuery to implement this dynamically (although I am certain you could use SASS/SCSS to achieve this dynamically as well).
The main focus of the script would be to check if the heading has the .e-active and not apply the .hover class in that instance. Once that's down, you can simply change :hover to .hover.
Here is an example - run the code snippet to see it work:
$('h1').mouseenter(function(){
if(!$(this).hasClass('e-active')) { //if heading does NOT have e-active class, apply hover effect
$(this).addClass('hover');
}
});
$('h1').mouseleave(function(){
$(this).removeClass('hover');
});
h1 {
background-color: salmon;
}
h1.hover {
background-color: teal;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Non active Heading 1</h1>
<h1 class="e-active">Active Heading 2</h1>
<h1>Non active Heading 3</h1>
EDIT:
Bhuwans answer that shows the use of the :not selector is a much cleaner way of achieving that - I would suggest using that route first whenever possible.

Conditional styles, hover and ReactJS

I have an anchor within a <div>, the anchor uses conditional styling for it's background and this seems make it so that the a:hover style gets discarded. Actually it doesn't matter if I have conditional or just a fixed color, if I remove the background-style from component.js the hover-effect from style.css kicks in.
My question is how do I achieve the hover effect while still having a conditional background-color?
component.js:
<div>
<a href="#"
style={{
background: (day === 2) && "#f1f1f1"
}} />
</div>
style.css:
div a {
display: block;
}
div a:hover {
background: blue;
}
This is a great use case for CSS stylesheet vs inline styles which will override due to css specificity.
Pop a class on it and do something like.
.conditional {
background-color: #f1f1f1;
}
.conditional:hover {
background-color: red;
}
Just to add, take a look at styled components https://www.styled-components.com/ . Its a matter of preference but using the style object can be a bit limiting and difficult to read (IMHO), some would say styled components is hard to read - your choice
There's lots more to styled components so definitely worth taking a look.
your use case would be something like below (or you could have your styled component in a different module)
import React from 'react'
import styled from 'styled-components'
const Link = styled.a`
display: block;
& :hover {
background: ${props => props.dayValue === 2 ? white : blue};
}
`
render(){
return (<div> <Link href='' dayValue={this.props.dayValue}/></div>)
}
One solution would be to use !important to force the :hover behaviour even when inline styles as you have are applied, like so:
div a:hover {
background: blue !important;
}
If dynamic inline styling is not required (ie you don't need a dynamic background color), then you'll typically want to avoid the use of !important in favour of class based styling:
JSX
<div>
<a href="#" className={ (day === 2) && "dayTwoClass" } />
</div>
CSS
div a {
display: block;
}
// Increased specificity of div to a causes blue background on hover
div a:hover {
background: blue;
}
// Lesser specificity causes f1f1f1 background when not hovered
.dayTwoClass {
background: #f1f1f1;
}

How can I override inline styles with external CSS?

I have markup that uses inline styles, but I don't have access to change this markup. How do I override inline styles in a document using only CSS? I don't want to use jQuery or JavaScript.
HTML:
<div style="font-size: 18px; color: red;">
Hello World, How Can I Change The Color To Blue?
</div>
CSS:
div {
color: blue;
/* This Isn't Working */
}
The only way to override inline style is by using !important keyword beside the CSS rule. The following is an example of it.
div {
color: blue !important;
/* Adding !important will give this rule more precedence over inline style */
}
<div style="font-size: 18px; color: red;">
Hello, World. How can I change this to blue?
</div>
Important Notes:
Using !important is not considered as a good practice. Hence, you should avoid both !important and inline style.
Adding the !important keyword to any CSS rule lets the rule forcefully precede over all the other CSS rules for that element.
It even overrides the inline styles from the markup.
The only way to override is by using another !important rule, declared either with higher CSS specificity in the CSS, or equal CSS specificity later in the code.
Must Read - CSS Specificity by MDN 🔗
inline-styles in a document have the highest priority, so for example say if you want to change the color of a div element to blue, but you've an inline style with a color property set to red
<div style="font-size: 18px; color: red;">
Hello World, How Can I Change The Color To Blue?
</div>
div {
color: blue;
/* This Won't Work, As Inline Styles Have Color Red And As
Inline Styles Have Highest Priority, We Cannot Over Ride
The Color Using An Element Selector */
}
So, Should I Use jQuery/Javascript? - Answer Is NO
We can use element-attr CSS Selector with !important, note, !important is important here, else it won't over ride the inline styles..
<div style="font-size: 30px; color: red;">
This is a test to see whether the inline styles can be over ridden with CSS?
</div>
div[style] {
font-size: 12px !important;
color: blue !important;
}
Demo
Note: Using !important ONLY will work here, but I've used
div[style] selector to specifically select div having style
attribute
You can easily override inline style except inline !important style
so
<div style="font-size: 18px; color: red;">
Hello World, How Can I Change The Color To Blue?
</div>
div {
color: blue !important;
/* This will Work */
}
but if you have
<div style="font-size: 18px; color: red !important;">
Hello World, How Can I Change The Color To Blue?
</div>
div {
color: blue !important;
/* This Isn't Working */
}
now it will be red only .. and you can not override it
<div style="background: red;">
The inline styles for this div should make it red.
</div>
div[style] {
background: yellow !important;
}
Below is the link for more details:
http://css-tricks.com/override-inline-styles-with-css/
used !important in CSS property
<div style="color: red;">
Hello World, How Can I Change The Color To Blue?
</div>
div {
color: blue !important;
}
!important, after your CSS declaration.
div {
color: blue !important;
/* This Is Now Working */
}
div {
color : blue !important;
}
<div style="color : red">
hello
</div>

Remove CSS effect from individual elements

I would like make all text within div.main gray except for all content within the child div.exception. div.exception should appear as if class main was never added to the parent div.
Is this possible? If so, how? Thanks!
<style type="text/css">
.main{color: gray;}
.hello{color: red;}
</style>
<div class="main">
<div>
<div class="exception"><p class="hello">Hello</p><a>Link</a></div>
</div>
<div><p>Howdy</p></div>
<div><a>Link</a></div>
</div>
for modern browser, just apply the rules to every div but .exception
.main div:not(.exception) p {
/* style for very nested div not exception */
}
otherwise override the rules later (as suggested by #jacktheripper)
This is simply done by:
.main .exception {
your styling here (e.g. color: black)
}
See this jsFiddle example
You cannot use color: inherit as this selects only the immediate parent, when you want to select two parents above. Therefore you have to override the colour 'manually'
#F. Calderan's answer is an alternative, but browser support is variable
No, that's not possible.
You can easily override the style so that it appears not to have been colored gray, but then you have to know what the original color was:
.main .exception { color: black; }
If you would set the style on the inner elements directly intead of on the main element, and set the exception class on the same level, you could override it using inheit:
<style type="text/css">
.main div { color: gray; }
.main div.exception { color: inherit; }
.hello { color: red; }
</style>
<div class="main">
<div class="exception">
<div><p class="hello">Hello</p><a>Link</a></div>
</div>
<div><p>Howdy</p></div>
<div><a>Link</a></div>
</div>

Resources