Light DOM styles "leaking" into Polymer component's shadow DOM - polymer

I have a polymer web component which has a div named content, within the component.
I have noticed that if the host page has a style for div.content then it applies that style to my component!
This is an unexpected turn of events since web components are supposed to prevent light DOM styling from leaking into the component. In addition, I am using the following css rule which is supposed to prevent such a thing from happening:
:host {
all: initial;
}
Any ideas?

Polymer by default doesn't use shadow-dom. It uses an approximation of it called shady dom, which doesn't provide style encapsulation and everything is dumped into light dom.
If you want to enable shadow dom by default, you can do so by using this config:
<script>
window.Polymer = window.Polymer || {};
window.Polymer.dom = 'shadow';
</script>

Related

Why can't I target my custom-defined HTML elements in CSS?

In my HTML, I've got:
<link rel="stylesheet" href="css/styles.css">
<script src="js/components/custom.js"></script>
...
<custom></custom>
In custom.js, say I've defined:
class Custom extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `
<style>
#import url('/css/styles.css');
</style>
...
`
}
}
customElements.define('custom', Custom)
In styles.css, when I define styles, they work. But I can't do something like
custom img {
}
to target an img inside custom. I have to use wrappers like div.custom-contents to target elements within.
Is this a known limitation perhaps with #imports like this, custom elements in general, or am I doing something wrong?
It is mandatory for custom elements that the name must consist of at least one letter, followed by a - dash, then at least one other letter. Your web component will not work if you don't conform to that requirement.
Your component uses shadow DOM. No outside styles affect the shadow DOM (other than inheritance), and no styles in the shadow DOM affect elements outside of of your component. This is the main concept behind shadow DOM - components bring their styling, and don't affect other elements on the page they're used in.
Also, you shouldn't attach the shadow DOM in the connectedCallback. That should always be done in the constructor.
You can use the part attribute along with the ::part pseudo element to allow styling from the outside.

Why does this globally defined css rule affect styling inside of the shadow DOM?

I created an web-component with shadow mode 'open', which is used like this:
<scu-switch checked="true" value="switch1">
<span id="scu-switch-label">Switch On</span>
</scu-switch>
and looks like this:
Than I added the button to a webpage with the following global CSS:
text-align: center;
and now the button style is broken:
When I inspect the button I can see, that the global style was applied to the span (and notice that it is not part of slot content) inside of the shadow root.
The shadow DOM is supposed to isolate style from the rest of the web page.
Why was this text-align: center applied here, even though it was defined outside of the Shadow DOM?
One of the great features of the Shadow DOM in Web Components is that styles are encapsulated to the component - you can style your component without worrying about any specifier (id, class, etc.) conflicts or styles 'leaking out' to other elements on the page.
This often leads to the belief that the reverse is true - that styles outside of the component won't cross the Shadow boundary and 'leak in' to your component. However this is only partly true.
While specifiers do not leak in to your component (e.g. a color applied to an p element in a style rule outside of your component won't effect any p elements in your Shadow DOM, although the rule will be applied to your Light DOM, or slotted content),
inheritable styles applied to any elements containing your component will be applied to both your Shadow and Light DOM.
Source: https://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/

How to style deep inside shadow trees

I have a problem with styling deep shadow trees. How can i apply some styles into shadow-dom ?
Here is i tried:
<link rel="import" href="../../bower_components/polymer/lib/elements/custom-style.html">
<custom-style>
<style is="custom-style">
vaadin-text-field [part='input-field']{
display: none;
}
</style>
</custom-style>
But its not working. Any advice ?
My understanding is that you can't, see:
https://www.polymer-project.org/2.0/docs/devguide/style-shadow-dom
"The HTML elements in your template become children in your custom element's shadow DOM. Shadow DOM provides a mechanism for encapsulation, meaning that elements inside the shadow DOM don't match selectors outside the shadow DOM."
Instead you need to extend the webcomponent, making custom version of it with your styles, the linked documentation gives information on that too.
you can use vaadin-themable-mixin to style parts elements.
But please note that once theme is loaded, it's will become global, so that all vaadin control in your website is styled by your theam.
read more at:
https://github.com/vaadin/vaadin-themable-mixin/wiki/1.-Style-Scopes

Shadow Dom and CSS3 :target selector

I should start off by saying that I don't really have an issue that I'm trying to work through. I just had an interesting thought about how Shadow Dom and the CSS3 :target selector might / should / currently do work together.
I know that HTML specification says that there should only ever be one element with a particular ID value in a valid HTML document. But when we start using webcomponents with shadow dom we could very easily find ourselves using multiple elements with the same ID. This is especially true when we use the same component multiple times in the same page. So the question that I have is this: what should happen to an element inside a shadow dom region that has an ID value which matches the current hash and which is styled with a :target rule?
For example, if I wrote a webcomponent (my-element) that contained
<style>
#debug {display:none}
#debug:target { display:block; background-color:yellow; border 2px solid red; }
</style>
<div id="debug">some debug data</div>
What should happen to all the instance of my-element that I put on a page and navigated to #debug on? Should the debug element in each component show? Should none of them show? Should only the first element's debug div show (the same one I'd expect the browser to try and navigate to)?
My opinion is that if the page does not have an element with an ID=debug value that no scrolling navigation should appear on the page. As shadow dom is isolated from the rest of the page's styles the browser shouldn't try to navigate to such an element nested in shadow dom. Each my-element instance should be able to see the current page's URL though and should apply any matching :target rules, such that each my-elements' debug div should be visible.
If this were the case it would make for some interesting possibilities for sharing page state across all components, such as the debug example above. However, I doubt that is how Chrome is currently implementing things. And I'm pretty sure this Shadow Dom polyfill isn't going to handle things correctly as it basically shoehorns everything into the page's Dom tree and that would break the html specification.
Just curious if anyone has an answer for how this should work and how it works today...
(edited from my pc to add formatting... hard to do from my phone)
I think you can see the shadow DOM like a nested document. CSS can't address elements inside the shadow DOM from the outside (previously existing shadow piercing CSS selectors were deprecated).
This also encapsulates ids and therefore multiple components that contain elements with an id won't cause collisions or duplicates.
If you have the CSS with the :target selector inside a components style, it should be able to address the element with the matching id, otherwise it shouldn't.
So the question that I have is this: what should happen to an element
inside a shadow dom region that has an ID value which matches the
current hash and which is styled with a :target rule?
Adding to Günter Zöchbauer above answer an alternative is to use the Custom Element object when the style is encapsulated, if the style is global it will work just fine. Use the define method to create a custom component as shown in the the docs. This will not encapsulate your elements so be aware that your styles can be shared across files.
So instead of doing this:
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(pTag)
Use this:
this.appendChild(pTag);
both previous examples suppose you're in a HTMLElement class or a class that inherits it.

Polymer custom element tag appearing in the DOM

I've just started learning about Polymer custom elements.
I've created a very simple custom element, the usage of which is as follows:
<my_custom_element></my_custom_element>
The template for the custom element is:
<template>
<span>Hello</span>
</template>
If I inspect the DOM using Chrome Dev tools, I notice that my_custom_element tag appears in the DOM. I wasn't expecting this. I was expecting that this tag would be replaced by the template content. What does my_custom_element represent in its own right?
I also read that :host can be used to style a custom element internally from within its definition and it's used to style host element itself. But again I don't understand what it means to style the host element in its own right. Isn't the host element defined by its template content?
The web components model does not use <my-custom-element> as a placeholder, but as an actual and real HTML element with complex behaviors and its own contents.