Share CSS styles across web components - css

I am using mozilla - X-tabs, web components in my application for tabbing it. Now, i need to use bootstrap - glyphicons in my app. But since webcomponents use shadow-DOM, bootstrap css styles are not applied inside web components?
Is there a way around it, to share a css file/style for all web components?

You possibly want to read CSS Scoping Model by W3C.
Each polyfill library (I’m sure, e.g., Mozilla’s does) provides handlers for /deep/, ::content etc selectors.
Hope it helps.

We faced a similar scenario with font-awesome. We had a bunch of classes that are common across web components. We loaded them directly from the host and made those classes available to components using /deep/ combinator (soon going to be replaced with >>> syntax)
The polyfill (Webcomponents.js - from Polymer) used by X-tags has support for /deep/ and ::shadow (single level). You can use these selectors to target shadow DOM from outside.

Polymer provides an element core-style for this, see here.
This blog post goes through how to set it up, you use the same tag to declare the common styles and to import them.
<core-style id="button">
button {
display: inline-block;
background: #bada55;
}
</core-style>
The presence of an id makes this element a producer of common styles.
Then to import the common styles inside the shadow dom of a component:
<core-style ref="button"></core-style>
The ref attribute makes this a style consumer.
So AFIK its not possible in a standard way yet with plain web components, but there's the core-style extension in Polymer, or the Polymer support for ::shadow and /deep/selectors.

Web components supports css stylesheet import:
<template id="template-for-your-component">
your web component
<link href='./.../share-rules.css' rel='stylesheet' />
</template>
You can have a global stylesheet shares between all your components without duplicate your rules.

Related

Host CSS classes made available to ShadowDom contents

As far as I can see even with mode=open CSS classes of host are not piercing through Shadow Dom.
Is it possible to make CSS classes defined outside ShadowDom available to its contents?
No, the whole point of shadowDOM is encapsulation.
If you don't want that, then do not use shadowDOM.
Web Components can be programmed to accept some Global styling with:
CSS Custom Properties
https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
::part
https://developer.mozilla.org/en-US/docs/Web/CSS/::part

Angular: Component styles do not cascade to sub-components?

In my app.component.html I have the following, working class assignment.
<body>
<span class="test">SHABOO</span>
<div class="container">
<router-outlet></router-outlet>
</div>
</body>
In the outlet, a component is rendered and it contains the following.
<span class="test">HAZAA</span>
I expected it to get the same style as the first one but it seems that the style doesn't cascade down to the component. It made me uncertain if I'm mistaken about how the styles are supposed to behave between parent and child components in Angular.
I made sure that the name of the class isn't overriden (to exclude the risk of collision). At the moment, I'm putting similar chunks of SCSS code in each component and that's obviously bad practice.
If I #import "../app.component.scss", the styles kicks in. But I was under the impression that even without the import, the style is supposed to cascade.
Angular components use view encapsulation. This is by design so that components are reusable across applications. To treat a component's styles as global, set the view encapsulation mode to none (not recommended).
Instead of using none, register global style files in the Angular CLI styles section, which is pre-configured with the styles.css file.
Component CSS styles are encapsulated into the component's view and don't affect the rest of the application.
To control how this encapsulation happens on a per component basis, you can set the view encapsulation mode in the component metadata. Choose from the following modes:
ShadowDom view encapsulation uses the browser's native shadow DOM implementation (see Shadow DOM on the MDN site) to attach a shadow DOM to the component's host element, and then puts the component view inside that shadow DOM. The component's styles are included within the shadow DOM.
Native view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - learn about the changes.
Emulated view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component's view. For details, see Appendix 1.
None means that Angular does no view encapsulation. Angular adds the CSS to the global styles. The scoping rules, isolations, and protections discussed earlier don't apply. This is essentially the same as pasting the component's styles into the HTML.
To set the components encapsulation mode, use the encapsulation property in the component metadata:
src/app/quest-summary.component.ts
// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native
ShadowDom view encapsulation only works on browsers that have native support for shadow DOM (see Shadow DOM v1 on the Can I use site). The support is still limited, which is why Emulated view encapsulation is the default mode and recommended in most cases.
External and global style files
When building with the CLI, you must configure the angular.json to include all external assets, including external style files.
Register global style files in the styles section which, by default, is pre-configured with the global styles.css file.
See the CLI wiki to learn more.
This is how angular works. Every component is separated with it's CSS, even if it is child component of a parent. They can't inherit their parent CSS.
Haza span component is child of your app component, but it is separated component. So, you can't get those Shaboo styling to Haza.
Solution:
if you want to maintain same CSS, there are multiple ways.
Recommended way:
a) if it is only for onle class, copy the class to the child component.
b) if it is for huge number of classes, create a css file for them and import them in both parent and child component css.
Example:
#import '../span.css';
you can add this css to styles.css. then you will get it anywhere in the app. if you want this style many places, you can do it.
As #Christopher Peisert mentioned, by using
encapsulation: ViewEncapsulation.Native
end of the #component decorator in child.component.ts. It is not recommended way
The global CSS files are those that are listed in angular.json. All others CSS files are encapsulated by default (this can be removed but is not recommended.)
Keeping your global CSS organised is important as your application grows. What I like to do is create a global styles folder and then add new files for different widgets that I might be altering. Since removal of ::ng-deep these types of changes have to be done in the global scope.
It works like that. If you are in need of having a common style then add it in the global styles.scss file.
If you want a global style put it in the global file styles.css or styles.[Your css preprocessor]
See this example https://stackblitz.com/edit/angular-a5ntm6?file=src%2Fstyles.css
I put test class at the end
.test{
color:red;
}

Where is this style attribute on my Angular router-outlet element coming from?

So I've got an Angular app that has pretty basic routing, and I'm using flexbox to layout my components. The issue I can't figure out is with code like this:
<div fxLayout="column"
fxFlex
class="layout__right">
<router-outlet></router-outlet>
</div>
The child component that is routed to contains the following in its SCSS:
:host {
#include make-flex-container(column);
flex: 1;
}
That make-flex-container just applies some flexbox related styles, and works fine in many places of the app. What's happening in my case though is when routing to this particular child component I see a style tag applied to the ng-component element created by Angular. What's causing my problem is for some reason the style includes flexbox items that are overriding what I'm putting in :host:
You can see in the screenshot that my :host styles are being applied, but the styles on the ng-component tag are simply overriding them. For the life of me I can't understand why there's a specific style tag added here, so where the content within it would come from. Does anyone know why Angular would put style tags on the HTML generated for router-outlets? When I navigate to other components at this same routing level this style tag isn't present.
I assume this is an issue with my code, but I just can't run down where to look given what I see.
UPDATE
Here's a minimal reproduction of the issue on Stackblitz:
https://so-48451609-routing-flex-issue.stackblitz.io
In that example you can see how the element created next to the router outlet has styles applied to it. The only dependency added there is #angular/flex-layout, so it's gotta be doing something to cause this.
The fxLayout directive applies styles to child elements. It does this on the element itself to not interfere with other styles.
The #angular/flex-layout library's static API has directives that work either on a DOM container or DOM elements. fxLayout is an example of the former, fxFlex is an example of the latter.
See the docs here, note the two sections for elements and containers: https://github.com/angular/flex-layout/wiki/Declarative-API-Overview#api-for-dom-containers
THEY COME FROM VIEW ENCAPSULATION
I believe that these classes are part of view encapsulation that prevents angular elements from being styled from outside and to prevent interference between view components. At least that is what they say in official docs #angular.io.
As suggested #this SO post the '_ngcontent-c0' is naming of the first component within the host.
ANGULAR MATERIAL IS CAUSING TROUBLES
I have found a bunch of issues related to encapsulation at GitHub and they often seem to be related to using Angular Material, which seems to have encapsulation turned off sometimes somehow. Such as described here. Hence I conclude that your implementation of Angular Material might be at fault. For in depth analysis of what the mechanism of this unwanted interaction is, you might probably like to start by reading joh04667's answer to your question since it provides links to the documentation of the particular material you use.
PROPER STYLING
Common solution to those issues is overriding styles with /deep/ selector as described in both above shared links. A number of unofficial articles on styling Angular app is available as well, such as this one.
IDEA
So the idea is to overwrite the styles in your component with /deep/ like this:
:host /deep/ .your_flex_class {
#include make-flex-container(column);
flex: 1;
}
CONSIDER VALID CSS
Since I think that you use Scss, using /deep/ selector should be ok and should be compiled successfully (I use it quite often to style inline SVGs). However, in a fact, this selector seems to be deprecated as a whole bunch of things related to the issue of Shadow DOM. I believe the correct universal approach would be to use 'shadow-piercing descendant combinator':
:host >>> .your_flex_class {
#include make-flex-container(column);
flex: 1;
}
It should does all the same as /deep/ selector. To explain the mechanism, let me cite from official docs for CSS Scoping Module Level 1
When a >>> combinator (or shadow-piercing descendant combinator) is encountered in a selector, replace every element in the selector match list with every element reachable from the original element by traversing any number of child lists or shadow trees.
Basically, it does not care about the wrapper and bites down to your target to style it. So you have even one more option to try ...
Ok, once I created a working example of this I eventually found the culprit and a suitable work-around. What is happening is within my router child component I'm using fxFlex from #angular/flex-layout to flex the element. I'm applying the flexbox container CSS in the :host{} section of the components CSS, but flex-layout doesn't see this in the rendered HTML and applies the style tag to do a flexbox row layout automatically.
Here's an issue on Github with my comments and a suitable workaround of using !important in my :host{} CSS:
https://github.com/angular/flex-layout/issues/496#issuecomment-360870510

How to use parent component's class in a child css selector

Consider the following hierarchy in some Angular 2/4 project:
<parent-cmp [ngClass]="{ 'parent': condition }">
<child-cmp class="child"></child-cmp>
</parent-cmp>
Now in the child component's CSS file I would like to say:
.parent .child {
background-color: red;
}
In this scenario, I'm basing the child's design on parent's logic without knowing what that logic is at child level. But the problem is that, this is not going to work. And that's because, Angular compiles the the child CSS selectors to this:
.parent[_ngcontent-c9] .child[_ngcontent-c9] {
background-color: red;
}
And the parent part of the selector is not going to work anymore. So How can I pull this off? Also please bear in mind that I simplfied this example and the two components are not necessarily one after another (there might be arbitrary number of components in between).
In Angular this is called "view encapsulation" where the JavaScript, CSS styles, and HTML templates are all managed by Angular. There are a lot of advantages to this approach as it allows you to easily tree-shake a project and drop components that are not being used. You not only drop the Javascript code, but all the styles and HTML with it.
When it's turned on the styles are injected into the DOM as embedded styles. Angular keeps track of what styles are required on the document and adds or removes styles as needed. These styles can have strange names at run-time like those in your question.
You need to read up on the https://angular.io/guide/component-styles styles guide to see how to define a :host style. This is the style assigned to a component when view encapsulation is turned on. When using :host you can refer to the parent selector using a :host-context path, and you can also style inside other child components using the ::ng-deep selectors.
Keep in mind. This is all optional. It's turned on by default, but if you don't want to use it. You can turn it off.
You can change the view encapsulation mode when you define your component. To disable this feature just change the encapsulation option to native.
See the guide:
https://angular.io/guide/component-styles#view-encapsulation

Styling shadow DOM with external

Given a webpage with dynamically loaded web components (shadow DOM) and some external CSS files (Bootstrap, etc.). I want these CSS files to be applied within the components (shadow DOM). What are the possible solutions?
Using Polymer -- we're already using AngularJS and don't wanna start messing with another framework too. (Although it's possible.)
Refactoring the CSS files and include /deep/ everywhere -- maintainability...
Import each CSS file manually into the beginning <style> part of each shadow DOM -- no comment...
Is there any better way to apply whole CSS files within shadow DOMs?
Question is a bit old, but putting this answer here in case it helps.
/deep/ has been deprecated
Other option is to use custom properties provided by Polymer.
If you don't want to use Polymer then as suggested in the discussion on the /deep/ and ::shadow selectors deprecation thread, you can you use css #imports.
You can generate the url of your external stylesheet and inject it in your shadow dom templates at run-time.
I have written a descriptive answer here on the topic.

Resources