Control CSS variable from Angular 5 - css

Is there any way to control a CSS variable defined at a component's root level using Angular methods? In JavaScript, we have document.documentElement.style.setProperty() when we set at root level.
In angular, can we use ':host' to declare css variable for global access within component? or do we need to use something like '::ng-deep :root'?
The following question also remains unanswered:
Angular: Use Renderer 2 to Add CSS Variable

Yes you can set variables in root scope:
:root {
--main-color: red
}
Yes you can use :host selector to target element in which the component is hosted.
:host {
display: block;
border: 1px solid black;
}
You can also use, :host-context to target any ancestor of the component. The :host-context() selector looks for a CSS class in any ancestor of the component host element, up to the document root.
:host-context(.theme-light) h2 {
background-color: #eef;
}
Note: ::ng-deep or /deep/ or >>> has been deprecated.
Read more about it here: special css selectors in angular
Just an additional information.
It works both inside ':root' as well as ':host'
We can set values to them by:
constructor(private elementRef: ElementRef) { }
then
this.elementRef.nativeElement.style.setProperty('--color', 'red');

The most constructive and modular way to use css vars in components (with viewEncapsulation) is as such:
// global css
:root {
--main-color: red
--alt-color: blue
}
// inside component component css
::ng-deep :root {
--specific-css-var: var(--main-color)
}
:host {
background-color: var(--specific-css-var)
}
:host(.conditional-class) {
--specific-css-var: var(--alt-color)
}
NOTE: despite ::ng-deep being deprecated, it hasn't been replaced yet (and has no replacement), as can be read in several discussion like this

Best thing for each component like a background color with out using ::ng-deep (which sets bg for all children)
import the following
import {ElementRef} from '#angular/core';
declare elementref in constructor
constructor(private elementRef: ElementRef) {}
then call the function ngAfterViewInit()
ngAfterViewInit(){
this.elementRef.nativeElement.ownerDocument.body.style.backgroundColor = ' white';
}
this sets bg to white but you can replace it with a hex color as well, so you can do that with every component

Related

Vaadin-flow: Css stylesheet import for custom components with shadow root element

I created a server-side component with a shadow-root element.. Is it possible to import a style sheet for the elements within that shadow-root? The CssImport annotation does not work, and I couldn't find anything similar, that could work?!
I could create a static String and add an element, but a css-file-import would be better?! (and of course I could use the component without a shadow-root, but the question was "is it possible" ... )
MyCustomComponent.java
#Tag("my-custom-component")
#CssImport("./components/my-custom-component.css")
public class MyCustomComponent extends Component {
public MyCustomComponent() {
super();
ShadowRoot shadow = getElement().attachShadow();
Span span = new Span();
span.getElement().setAttribute("part", "caption");
Div div = new Div();
div.getElement().setAttribute("part", "content");
shadow.appendChild(span.getElement());
shadow.appendChild(div.getElement());
}
}
my-custom-component.css
:host [part='caption'] {
background-color: red;
}
:host [part='content'] {
background-color: blue;
}
I'm curious why you would want a shadow root around a Flow component, as it doesn't really provide any benefits other than CSS encapsulation.
The #CssImport annotation with the themeFor parameter won't help you in this case, as that only works with Web Components using ThemableMixin (https://github.com/vaadin/vaadin-themable-mixin/).
I'm not sure whether it's possible to load css into a shadow root with Flow, but as long as you have part attributes on all elements you want to style, you can do that with a regular (non-shadow-dom) stylesheet, like so:
my-custom-component::part(caption) {
color: red;
}
Just put that in your styles.css or wherever you have your app's normal global css.

change width in ::before property using angular and css [duplicate]

Is there any way to control a CSS variable defined at a component's root level using Angular methods? In JavaScript, we have document.documentElement.style.setProperty() when we set at root level.
In angular, can we use ':host' to declare css variable for global access within component? or do we need to use something like '::ng-deep :root'?
The following question also remains unanswered:
Angular: Use Renderer 2 to Add CSS Variable
Yes you can set variables in root scope:
:root {
--main-color: red
}
Yes you can use :host selector to target element in which the component is hosted.
:host {
display: block;
border: 1px solid black;
}
You can also use, :host-context to target any ancestor of the component. The :host-context() selector looks for a CSS class in any ancestor of the component host element, up to the document root.
:host-context(.theme-light) h2 {
background-color: #eef;
}
Note: ::ng-deep or /deep/ or >>> has been deprecated.
Read more about it here: special css selectors in angular
Just an additional information.
It works both inside ':root' as well as ':host'
We can set values to them by:
constructor(private elementRef: ElementRef) { }
then
this.elementRef.nativeElement.style.setProperty('--color', 'red');
The most constructive and modular way to use css vars in components (with viewEncapsulation) is as such:
// global css
:root {
--main-color: red
--alt-color: blue
}
// inside component component css
::ng-deep :root {
--specific-css-var: var(--main-color)
}
:host {
background-color: var(--specific-css-var)
}
:host(.conditional-class) {
--specific-css-var: var(--alt-color)
}
NOTE: despite ::ng-deep being deprecated, it hasn't been replaced yet (and has no replacement), as can be read in several discussion like this
Best thing for each component like a background color with out using ::ng-deep (which sets bg for all children)
import the following
import {ElementRef} from '#angular/core';
declare elementref in constructor
constructor(private elementRef: ElementRef) {}
then call the function ngAfterViewInit()
ngAfterViewInit(){
this.elementRef.nativeElement.ownerDocument.body.style.backgroundColor = ' white';
}
this sets bg to white but you can replace it with a hex color as well, so you can do that with every component

Angular change child component style from parent component but not globally

I have created a shared component(<nextgen-table></nextgen-table>) based on Mat-table (Angular Material). While using this component inside a project, discover that I need to change the behavior(width) of the table columns.
I have exported nextgen-table in my other components (let's say X, Y ) where nextgen-table is, of course, a child component.
To change the width of specific columns of the mat-table I have to use something like this:
mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
flex: 0 0 40%;
text-align: left;
}
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
flex: 0 0 20%;
}
The above CSS code I was implementing in the X.component.css and it was not working because of encapsulation I guess.
After a little bit of search, I have found the solution which worked correctly just by adding the encapsulation: ViewEncapsulation.None in the Component decorator of x.component.ts. After this solution, I was navigating from the component X to the Component Y in which I didn't implement the above CSS code. But the component Y had the first two columns as I wanted only for component X but somehow component Y had also which I didn't want for the component Y.
So my question is how can I update the style of nextgen-table from the parent component which only applies for the parent component and not in the other components.
I have also tried to use
:host(mat-cell:nth-child(1)){
flex: 0 0 40%;
text-align: left;
}
:host(mat-header-cell:nth-child(1)) {
flex: 0 0 40%;
text-align: left;
}
but nothing happened/changed.
Thanks in advance for the help
You can use the ::ng-deep pseudo class, to specifically target child elements without changing the view encapsultation for the whole component (which would mean that all its rules would leak).
Note: ::ng-deep has been marked as deprecated for since a few major versions now, but they will not remove suppoprt until they have a workaround.
parentX.html
<div class="compContainer">
<nextgen-table></nextgen-table>
</div>
parentX.scss
::ng-deep .compContainer nextgen-table
{
mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
flex: 0 0 40%;
text-align: left;
}
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
flex: 0 0 20%;
}
}
You could also add your css rules to the global style.scss file.
//Rules for parent X
app-parent-componentX .compContainer nextgen-table
{
mat-cell...
}
//Rules for a parent Y
app-parent-componentY .compContainer nextgen-table
{
mat-cell...
}
All you need to do is use both :host and ::ng-deep pseudo-class selectors in your X or Y component.
Here is the working demo.
And here is the quick explanation.
styles which are written for the <nextgen-table> inside let say nextgen-table.component.css are get encapsulated by angular by adding a specific attributes for each style. i.e if you have written something like,
.mat-header-cell{
background-color: #ff0000;
}
then it becomes something like,
.mat-header-cell[_ngcontent-c29]
background-color: #ff0000;
}
So all we need to do is to override this style inside our component X or component Y.
We have ::ng-deep pseudo-selector which will prevent angular from encapsulating out component's css.
But using ::ng-deep will leak our css on to parent components as well. So to prevent that we need to encapsulate out ::ng-deep style. to do that we can use :host pseudo-selector.
so if we write following css inside component X,
:host ::ng-deep .x-table .mat-header-cell{
background-color: lightblue;
}
then it will become something like,
[_nghost-c82] .x-table .mat-header-cell {
background-color: lightblue;
}
now this above css selection has higher precedence than the style written in the table component .mat-header-cell[_ngcontent-c29].
That's how we can override child component's style inside any parent component.
I hope this will help.
Update:
As you can see in Angular's official docs that ::ng-deep is deprecated.
The shadow-piercing descendant combinator is deprecated and support is
being removed from major browsers and tools. As such we plan to drop
support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until
then ::ng-deep should be preferred for a broader compatibility with
the tools.
So if you don't want to be dependent on ::ng-deep than,
You can use ViewEncapsulation.None in your <nextgen-table> table component which you have already tried. Demo here
And to prevent the style from bleeding into other components you can scope the table's style by adding the selector in front of all the styles like this.
nextgen-table .mat-header-cell{
background-color: #ff0000;
}
and then you do the same for your X component.
Disable view encapsulation using ViewEncapsulation.None
Then override styles on table component by writing styles that have higher specificity than the table's actual style.
disable encapsulation in side your X component,
#Component({
selector: "app-x",
styleUrls: ["x.component.css"],
templateUrl: "x.component.html",
encapsulation: ViewEncapsulation.None
})
export class XComponent {
}
then override the table's component style in x.compoent.css
app-x nextgen-table .mat-header-cell{
background-color: lightblue;
}
If you don't want to disable the view encapsulation then you can write styles directly into global stylesheet styles.css.
Just remember that It's all about overriding and scoping your styles.
The only reliable way i've ever found is to use ::ng-deep
Anything else seems to be "hit or miss" intermittently

matTooltipClass not applying css

I am attempting to apply some css changes to mat-tooltip from angular material 2 and found in the documentation a matTooltipClass that can then be selected in the css file to make changes. However, I am not able to get it working.
component.html :
<mat-cell
*matCellDef="let productInfo"
matTooltip="{{productInfo.description}}"
matTooltipClass="tooltip">
{{ productInfo.description}}
</mat-cell>
component.scss:
.tooltip {
background-color: red;
color: blue;
font-size: 20px;
}
You have to use ::ng-deep to override default CSS for material elements:
::ng-deep .tooltip {
background-color: red;
color: blue;
font-size: 20px;
}
In addition to what was stated above,
Here are two methods that worked for me:
-in the Component.scss:
::ng-deep mat-tooltip-component{
& .mat-tooltip{
color: green; // your custom properties here.
}
}
-Globally:
.mat-tooltip{ // making the font size on the mat-tooltip 1.5rem globally
font-size: 1.5rem;
&.exaggerated-tooltip{ // to modify the tooltip create a class like this
font-size: 2.5rem; // and use it like this: *matTooltipClass="exaggerated-tooltip"* in the
color: red; // component in which you are putting the tooltip
}
}
A blog post by Siderite (Styling Angular Material tooltips) provided an answer that worked for me. I am paraphrasing from the most-relevant portion of his post and I am using the matTooltipClass="tooltip" scenario described in the Question above:
[The .tooltip class definition] should either be in the global CSS
file (so it applies to everything) or your component should declare
encapsulation ViewEncapsulation.None. [If the .tooltip class
definition is in the global CSS file], then ensure the declaration of
the class is specific enough: try this:
mat-tooltip-component .mat-tooltip.tooltip {...}.
In my case, I had defined the .tooltip class in the global styles.scss file, and it wasn't working until I followed Siderite's suggestion and defined it like this:
mat-tooltip-component .mat-tooltip.tooltip {
color: blue !important;
}
This approach is avoids using ::ng-deep as suggested in the accepted Answer. Angular documentation states that approach is deprecated. I did find I needed to use !important, which some believe is bad style.
Angular material doc says matTooltipClass supports the same syntax as ngClass.
thus you might try [matTooltipclass]="'tooltip'"
<mat-cell
*matCellDef="let productInfo"
matTooltip="{{productInfo.description}}"
[matTooltipclass]="'tooltip'">
{{ productInfo.description}}
</mat-cell>
From the example provided in the website (https://material.angular.io/components/tooltip/examples):
import {Component, ViewEncapsulation} from '#angular/core';
/**
* #title Tooltip that can have a custom class applied.
*/
#Component({
selector: 'tooltip-custom-class-example',
templateUrl: 'tooltip-custom-class-example.html',
styleUrls: ['tooltip-custom-class-example.css'],
// Need to remove view encapsulation so that the custom tooltip style defined in
// `tooltip-custom-class-example.css` will not be scoped to this component's view.
encapsulation: ViewEncapsulation.None,
})
export class TooltipCustomClassExample {}
If you stick that encapsulation: ViewEncapsulation.None line there for your component where you want to use your custom tooltip class, it ought to work. It's how I fixed the issue on my end.
Seeing that ::ng-deep is depricated now, best seems to be to add encapsulation: ViewEncapsulation.None, in your component decorator under your styleUrls line
Just be careful that all your css classes will be global then, so make sure your classes in that component have unique names if you do not want to overwrite the classes in other components accidentally
I found out there is a div element down the body with class="cdk-overlay-container". I considered its z-index. it was 1000 below the z-index of my modal. I made it the same as my modal's z-index, i.e. 1050. And it is working. I also had to put ::ng-deep before my class in CSS file.
::ng-deep .cdk-overlay-container {
z-index: 1050;
}
In case your project prohibits to use: ::ng-deep, you can use
encapsulation: ViewEncapsulation.None
in the TS file.

What does :global (colon global) do?

In some SCSS files, I see the following:
:global {
/* ... */
}
I don't know if it is an SCSS feature or a CSS feature.
I tried searching about it but couldn't find any good results at first sight.
The :global operator is used in CSS Modules. Modular CSS uses a CSS Modules compiler to scope CSS styles within their respective modules (e.g., React component).
Here's an example from a React module (in the file ErrorMessaging.less for the ErrorMessaging.jsx React component):
:global(.ocssContainer) {
.ui_column {
padding-left: 0;
}
}
This gets compiled into:
.ErrorMessaging__alertContainer--1I-Cz .ocssContainer .ErrorMessaging__ui_column--3uMUS {
padding-left: 0;
}
But now I add a :global modifier onto .ui_column:
:global(.ocssContainer) {
:global(.ui_column) {
padding-left: 0;
}
}
And this is what it compiles to:
.ErrorMessaging__alertContainer--1I-Cz .ocssContainer .ui_column {
padding-left: 0;
}
Now .ui_column can apply to any child element with that style, including in a child React component, and not just .ui_column elements that are part of the ErrorMessaging React component.
It looks like they are using CSS Modules. If you follow the docs they say:
:global switches to global scope for the current selector resp.
identifier. :global(.xxx) resp. #keyframes :global(xxx) declares the
stuff in parenthesis in the global scope.
The :global selector keyword is used in css modules to specify that a class should not be scoped to the component in which it is defined. This selector allows the class to be used globally in the application, rather than just within a specific component. For example, let say you have a .is-global-class class defined in a CSS Module file, you can use :global(.is-global-class) to apply that class to an element and make it available globally.

Resources