Can Shadow DOM elements inherit CSS? - javascript

I was listening to this episode of JavaScript Jabber:
http://javascriptjabber.com/120-jsj-google-polymer-with-rob-dodson-and-eric-bidelman/
At one point Rob says:
And everyone has this first inclination, because it makes so much sense. You’re like, “Bootstrap is components. I’m just going to make them into tags.” But then you run into the fact that the Bootstrap style sheet is just one big long style sheet that was written assuming that it could touch every part of the document. And when you are suddenly scoping bits of the markup, scoping it so that the CSS can’t reach it, the CSS would actually have to be in the Shadow DOM with it and you would have to write that element from the ground up, that’s where people I think get really confused and really frustrated initially.
This made me wonder, how would you solve this problem with Web Components? Is there a way for Shadow DOM templates to inherit common styles, or would you have to repeat shared CSS for each separate component? Or something else?

Note: the substance of the answer below is no longer relevant, as the features discussed have been deprecated for some time. Don't use the sample code, but feel free to take a peek into the Internet past.
In a complete Shadow DOM implementation, CSS has a ::shadow pseudo-class, and also the /deep/ combinator.
The ::shadow pseudo-class lets you break into the shadow DOM under an element, and it matches the shadow root. The /deep/ combinator effectively opens up the shadow DOM completely.
Thus, if you had a <x-foo> element with <span> elements inside, you could make them red with
x-foo::shadow span { color: red; }
Or make all <spans> in any shadow DOM red:
body /deep/ span { color: red; }

Whatever hacks were necessary in the past, the current state of the shadow dom / CSS is that you can link to external stylesheets. More info at MDN: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM#Internal_versus_external_styles
You could, then, have some sort of utility stylesheet (Tailwind or similar) that all of your components reference, so you don't duplicate CSS. In theory, that CSS file would get downloaded once, cached, and then the custom elements would each be able to use the classes in it (as long as they each link to it).

Take a look at this lesser known method:
The <core-style> Polymer element
You can define the styles in an html import file:
<core-style id="x-test">
:host {
backgound-color: steelblue;
}
</core-style>
And then you can use them in more than 1 element:
<polymer-element name="x-test" noscript>
<template>
<core-style ref="x-test"></core-style>
<content></content>
</template>
</polymer-element>
In this well written article you can read more about how to use the technique.
However, a disadvantage I can think of is the impossibility of using SASS with this technique, since the styles are defined inside a <core-style> tag and not inside a <style> element, and there isn't a clear way to import an external stylesheet.

I think I understand what you ask about. You can use link in every custom element to include your main styles or extend existing element that includes it. For example (using Polymer):
<polymer-element name="ui-button" noscript>
<template>
<link rel="stylesheet" href="main.css"/>
<div class="class-from-main" style="">
<content></content>
</div>
</template>
</polymer-element>
I think it worth reading to you: https://github.com/necolas/normalize.css/issues/408

Here's one example of it working : http://jsbin.com/zayih/1/edit?html,css,output
Though it does not seem to work when targeting it with #placeholder
Turn on "Show user agent shadow DOM" in Chrome devtools config tab, then inspect the <input> to see what I mean

Related

Shadow DOM/Web component styling in Ionic 4

I'm trying to understand how and where ionic 4 is injecting the styling for the web components into the dom. I'm not interested in changing it via the CSS variables but I want to be able to inspect the component and see where the styling is coming from. For example on https://ionicframework.com/docs/demos/api/alert/index.html?ionic:mode=ios
If you inspect the button:
I can see the stlying being applied but I can't see where all the styling is coming from, it's not in the css bundle, style tags on the page or directly on the element or applied to the shadow-root. Normally the host styling is part of the component e.g.
So where is the :host styling being defined and how/where is it being injected into the DOM?
So as far as I know all the styles you apply to your component are usually set innerHTML. You already found the style tag which is the first tag after the component. This is super full with stylings as always thats why you usually see just a snippet. That depends on the browser but to see all the stylings you may have to do a double click into the stylings between the style tags.
Usually when I want to read something like this I copy it into my editor and take a look there:
As you can see all your stylings are defined there. Stenciljs also load some general stylings to make sure everything looks proper. These are instantiated into your head section:
So I think Ionic 4 is using: https://developers.google.com/web/updates/2019/02/constructable-stylesheets
Which is why the styling isn't visible directly in the DOM. Credit to Fraser for working this out.

How do detect what overrides css properties of ReactJS components?

:)
I have been developing a webpage for some time now, and I decided to use a react-calendar component from here. In the beginning, when page was lightweight it worked out of the box and, following the example on the webpage, my page rendered this:
Later on in the development, as i added more complexity to the page, calendar became completely disfigured and only displays plainly, like this:
I thought something was overriding my calendar's css properties and tried putting it outside the app div like this:
<div>
<Calendar/>
<App/>
</div>
But calendar's appearance remains unchanged. After inspecting elements in browser I noticed some suspicious crossed out values:
Is there a way of preventing parent override of calendar's css properties? Or am i missing something crucial in code which would make the calendar look neat again? It always looks neat if I install and use it in a clean project (but i'd love to have a working one everywhere :D).
Thanks in advance for your advices! :)
Have a nice day!
If you have a CSS property you can almost always override it. There is no way to tell (for example using javascript) if a css value has been overridden, since it's the browser who does this. Maybe there is something in your code that changed the behaviour by adding a parent class that overrides all its children, or a major CSS style sheet like bootstrap that overrides some others. If this is your case, it's hard to tell without looking at the code.
Good news
You can completely prevent CSS inheritance, by wrapping your component in an iframe.
Or
You can have properties that can almost never be overridden, depending on CSS specificity.
According to MDN web docs:
Inline styles added to an element (e.g., style="font-weight:bold")
always overwrite any styles in external stylesheets, and thus can be
thought of as having the highest specificity.
Have a read at the complete article to understand how it works. This statement though, says that you can always override properties if they are set inline. Now, the problem of using a third-party component is that you cannot set the styles for everything used by the component, and of course the point on using a third-party component is that you don't need to bother about these configurations.
Apart from using inline styles, you can also set the !important flag to your CSS styles, but we come to the problem of the third-party component again.
I suggest these approaches:
1) Create CSS rule to reset values to initial
Basically create a rule called NoInheritanceCalendar for example, that will reset any rules before the calendar's rules.
Set your code to:
<div className="NoInheritanceCalendar">
<Calendar/>
</div>
and in your CSS:
div.NoInheritanceCalendar {
all: initial;
}
2) Wrap it in an iframe
Although not the best approach, this is the only way you could prevent inheritance at all.
3) Copy component's style sheet, add prefix class to all properties so that they are more specific.
If your third-party component's CSS is:
.rule1 {
font ...
width ...
}
.rule2 {
font ...
width ...
}
.rule3 {
font ...
width ...
}
You can create a prefix class to make those more specific:
.MyClass.rule1 {
font ...
width ...
}
.MyClass.rule2 {
font ...
width ...
}
.MyClass.rule3 {
font ...
width ...
}
and add your component in react as:
<div className="MyClass">
<Calendar/>
</div>
These tools help you with this if it is a big style sheet:
autoprefixer
prefixfree
less-plugin-autoprefix

How to scope internal Vue application css from rest of the website?

I need to create a "plugin" that contains a user interface which will be displayed on many different vendor websites. This is a CMS agnostic plugin. I cannot use an iframe for SEO reasons. I need to isolate the plugin's css (and maybe js) from the rest of the website, and stop the rest of the website's css from getting to this plugin. How can I do this?
Update:
Ok, so I've asked a question that's a little too specific to my setup/tech. The question should have been: How do I isolate an html element from the rest of the document styles? This is answered here;
New Question: How do I scope Vue CSS so that it doesn't propagate up, but propagates to child components?
E.g I have the main Vue component which includes bootstrap.scss, i need that to apply to all child components, but I don't want it to leak into the main website. Adding scoped to style stops the leak upward, but I want it to apply to child classes as well.
Ok, I've figured it out.
Pretty simple really, combined with this answer to prevent parent -> child inheritance. I scoped all Vue css into #app { /*styles*/ } INCLUDING the bootstrap import. E.g.
<style type="text/scss" lang="scss">
#app {
#import '../node_modules/bootstrap/scss/bootstrap';
// rest of vue global styles go here.
// child components may use scoped
}
</style>
Note: I am NOT using scoped attribute on the root vue component.
I think this is what you’re looking for. In your .vue file you can add style tags to the template, then Vue will create Shadow DOM styles that only apply to your application. In the final product the styles are rendered via a data-v attribute to prevent class name conflicts.
https://vue-loader.vuejs.org/en/features/scoped-css.html
(Copied from my reddit answer)
https://www.reddit.com/r/vuejs/comments/76ss46/how_to_isolate_a_vue_application_from_the_rest_of/?st=J8UMA1JQ&sh=c3ebf5b1

style and script tag sequence

Normally HTML page contains following tags
<script>
<link>
<style>
I found number of times that changing the sequence of those tags, mess up with page layout.
So what would be the reason and what are the points to avoid this situation?
EDIT
After looking the answer of #Anurag, I'm actually assuming that we don't have a case where we have two definition of the same css class, in different style or link tag.
My major concern is css and script sequence. Whether we should have all the css class before we write any JavaScript or it doesn't matter at all (that I don't think).
For example jqtouch floaty extension.
In that if I define the .floaty class before the JavaScript tag, then it don't work.
Hope you get my point.
The order of the <link> and <style> tags is most important in this case.
The <link> tag(s) will hold the reference to your style sheets, where you defined most of your page layout.
The <style> tag will define exceptions or additions on the definitions made in your style sheets.
So, you first need to know what's the default, before you can add something or make an exception on it. That's why the <link> tag(s) should appear before the <style> tag(s).
The style definitions (sheets and inline) are applied in sequence. The last definition overrides previous definitions.
Then we also have the <script> tag(s). These have nothing to do with messing up your layout.
As a rule of thumb, I always declare them after my <link> tags. Why? First show your visitor a nice looking page, the scripts should be used to support additional functionality.
I can't tell you specifically without seeing more of your HTML. However, if I may make a recommendation, I would suggest not inlining any of the style or scripts into your HTML. There are a number of reasons why you don't want to do this, but other articles and websites do a much better job of explaining why you don't want to.
And, by not inlining, it may fix the problems you are currently experiencing.
Order is important for all of these tags.
<script> tags are executed sequentially unless using the async or defer attributes, so a script tag that appears later in the page can override the functions/variables/.. that were previously declared.
Likewise stylesheets are applied sequentially and have specificity rules about how to handle conflicts etc. For instance, the style attribute takes precedence over everything and if a style appears later, then it overrides the previous style. For instance,
<style>
.page {
background-color: #CCC;
}
</style>
<style>
.page {
background-color: #222;
}
</style>
The color of the page will be #222.

How do I prevent CSS interference in an injected piece of HTML?

I'm currently developing a Safari extension that uses an injected script to further inject some HTML into the current webpage, as well as injecting some other scripts to make it work. This is all working fine, but the issue is that the HTML that is injected gets affected by CSS stylesheets that the webpage has already imported. For example, the HTML looks perfect on Google.com (which has relatively little CSS styling), but awful on StackOverflow.com (which styles buttons etc).
jQuery is injected into the webpage at the time of this HTML being displayed, so I have that available. I've tried all kinds of things, including walking through all of the elements and calling removeClass() on each of them, to no avail. I've also tried to add "CSS reset" classes, etc, but nothing seems to be working.
What's the best way to go around preventing the CSS from interfering with my HTML?
You can't prevent that from happen. However, you can override the CSS rules. Give your main element a unique id (which really should be unique by obfustation, like "yourapplicationname_mainelement_name" or something), then override all possible styles that might give strange effects on your html.
Your plugin:
<div id="yourapplicationname_mainelement_name">
<p>My paragraph that must not be styled</p>
</div>
Your css:
#yourapplicationname_mainelement_name p {
display: block;
color: black;
background: white;
position: relative;
... and so on ...
}
As your css style rules are the most specific, given your id, they will override any settings present on the page where your html is injected.
Further... It might be hard to see what rules are the most important. You can use firebug or similar to understand which is overriding another. You'll have a hard time without it when developing your application.
that's a tough one. two options as I see it.
You could set a wrapping div around all your content and prefix all your css with that. example:
<body>
<div class='wrappingDiv'>
...
</div>
</body>
stylesheet:
.wrappingDiv * {}
Then when you inject jquery use that to close off the initial wrapping div before your content and to wrap any following content in the another wrapping div.
Issues:
Only possible if you are injecting
other site content onto your own
site.
This could get complicated
depending on where you are injecting
html.
The other option is to load a resetting stylesheet that targets your injected html specifically. In this case only your injected html would be wrapped but you'd need a css file that reset all attributes for all tags to their default before you add your own styles. No real issues here, just not very elegant...
Another way would be to use an element that doesn't inherit stylesheet like an iframe, but that comes with its own issues...
i have seen on different plugins that they put the code inside a iframe and they use JS to interact with the rest of the page, so you can not change the css inside.
Also i have seen that when injecting html code,people sets the style of the plugin content using the "style" attribute inside the tags so the browser will give priority to the css inside the style attribute and not the css file. The idea is to override the css,usually with the "!important" clause. But you might have some problems on different browsers
EDIT i forgot to say that my answer is on the case that you inject the code on someone's else page where you cannot control directly the css

Categories