Can Lit or Web Components know attribute of parent component? - web-component

I have code like this:
<theme-component theme="dark">
<my-component></my-component>
</theme-component>
Is it possible for my-component to know it is within theme-component with a theme of dark? Or do I also need to pass theme=dark manually into my-component?
my-component has styles defined like this:
static darkStyle = css`
:host {
--my-bkg: #535353;
--my-items: #474747;
--my-gutter: #4a4a4a;
--my-border: #474747;
--my-item-border: #535353; /* on dark themes only this is the same color as the background instead of the footer border */
--my-divider: #636363;
}
`;
static lightStyle = css`
:host {
--my-bkg: #b8b8b8;
--my-items: #c7c7c7;
--my-gutter: #ababab;
--my-border: #adadad;
--my-item-border: #adadad; /* on light themes only this is the same color as the footer border */
--my-divider: #9e9e9e;
}
`;
static styles = [this.darkStyle, sheet];
I would love to have some kind of ternary to switch between this.darkStyle or this.lightStyle. Or is there some kind of CSS I can write that is like
static styles = [css`
theme-component[theme=dark] :root {
--my-bkg: #535353;
}
`, sheet];
I see documentation about SuperElement.styles, but it's not clear to me how to use that when I am relying on variables based on an attribute here. I'm not trying to share a style as much as use the attribute to determine some new variables.

Edit after clarifications:
A component could technically find out the attribute on a direct parent component by imperatively doing the following:
this.parentElement.getAttribute('theme').
I am not sure how that could be done in CSS. Below this edit I've outlined a common approach to solving the issue of theming using CSS custom-properties which have the default behavior of inheriting through the shadow DOM. Also see video by Lit team: "How to style your Lit elements" for more context.
Original answer:
I think what you're looking for is the :host() CSS pseudo-class function.
Your setup is correct where the theme-component is providing CSS custom properties that inherit to all children. my-component does not need any changes.
The only change from your example that should be needed is:
static darkStyle = css`
:host([theme="dark"]) {
--my-bkg: #535353;
--my-items: #474747;
--my-gutter: #4a4a4a;
--my-border: #474747;
--my-item-border: #535353;
--my-divider: #636363;
}
`;
This will select and apply the dark theme CSS custom properties when the theme-component has the theme attribute set to value "dark". These custom properties will then inherit into the children.
Runnable sample:
<script type="module">
import {html, css, LitElement} from "https://cdn.jsdelivr.net/gh/lit/dist#2/core/lit-core.min.js";
class ThemeEl extends LitElement {
static styles = css`
:host {
--my-bkg: green;
display: block;
border: 2px dotted var(--my-bkg);
}
:host([theme="dark"]) {
--my-bkg: gray;
}
`;
render() { return html`<slot></slot>`; }
}
class ChildEl extends LitElement {
static styles = css`
:host {
display: block;
background-color: var(--my-bkg);
}
`;
render() { return html`<p>Child El</p>`; }
}
customElements.define('theme-el', ThemeEl);
customElements.define('child-el', ChildEl);
</script>
<theme-el>
<p>Default Theme</p>
<child-el></child-el>
</theme-el>
<theme-el theme="dark">
<p>Dark Theme</p>
<child-el></child-el>
</theme-el>
This technique can also be used such that a custom element can change its own host styling. By setting attributes on itself and using :host().
If the child component also needs to know what theme is set from JavaScript, that could also be communicated via a CSS custom property and queried with window.getComputedStyle(this).getPropertyValue(<custom property>).

Related

scss wrong selector being matched

I have two components called GroceryItem and BasketItem. Since these components share quite a few styles and I didn't want to write the same properties in different stylesheets, I set up a stylesheet called _item-layout.scss that has all the common styles and then imported this in both BasketItem.scss and GroceryItem.scss.
The core difference between these components is that the BasketItem icon is meant to switch from white to red when the enclosing div is hovered upon, whereas the GroceryItem icon is meant to switch to green. To enable this functionality, I set the hover property to change colour in the BasketItem and the GroceryItem stylesheets.
The thing is, both components change icon colour to red when hovered on. This is especially confusing for me because in my GroceryItem.js file, I've only imported GroceryItem.scss. So how can it match a selector that isn't even present in the stylesheet that has been imported?
item-layout.scss:
.root {
background-color: rgba(230, 230, 230, 1);
margin: 10px 0px;
user-select: none; //standard syntax
-webkit-user-select: none; //webkit (safari, chrome) browsers
-moz-user-select: none; //mozilla browser
-khtml-user-select: none; //webkit (konqueror) browser
-ms-user-select: none; //IE10+
.p {
font-weight: 500;
}
}
.icon {
margin-right: 10px;
color: whitesmoke;
}
.row-content {
display: flex;
padding: 15px;
}
GroceryItem.scss:
#import "./../../styles/_item-layout";
.root:hover .icon {
color: green;
}
BasketItem.scss:
#import "./../../styles/item-layout";
.root:hover .icon {
color: red;
}
GroceryItem.js:
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlusSquare } from "#fortawesome/free-solid-svg-icons";
import "./GroceryItem.scss";
import React from "react";
function GroceryItem(props) {
return (
<div className="root" onClick={props.onClick} >
<div className="row-content">
<FontAwesomeIcon icon={faPlusSquare} className="icon"/>
<p>{props.title}</p>
</div>
</div>
)
}
export default GroceryItem;
I think the problem is that you have hover styles for the root class and you're using this class in both components.
If you import BasketItem.js or GroceryItem.js, their respective scss files will also be imported. Each of these scss files sets the color of elements with class root on hover to either red or green. So whichever js file is imported last will indirectly decide the root class hover color.
Since you say both components have a red hover color BasketItem.js is probably imported after GroceryItem.js based on the code in your question. If you switch the import order around you will probably see both components turn green.
The simple solution here is to just use separate classes for GroceryItem and BasketItem so there is no collision / involuntary overwriting of styles.
Both declarations of icon styles are in the same scope so the last loaded scss file overwrites the first one.
You could add an additional classname in both components and use them as a kind of color flag. Lets say in first component you add the css classname green next to your icon classname and in the second one add red next to your icon classname. So you have:
... className="icon green" ... and
... className="icon red"
In you main scss file you do
.root {
.icon {
&:hover {
&.green {
color: green;
}
&.red {
color: red;
}
}
}
}
Of course add the other styles you have set for your root class

Angular Dialog Background White

The background is only showing white by default, are there any CSS codes that I can force it to blur or darken the background instead? I've tried using CSS but I was unable to find the class it was called, I've put the "hasBackdrop" attribute to false as well but it didn't affect anything.
CSS Code
::ng-deep .mat-dialog-container {
background-color: #fff;
color: #fff;
overflow: hidden;
}
p {
color: black;
}
HTML
<p>test-modal works!</p>
Code to call the modal
export class CallModalComponent implements OnInit {
constructor(public dialog: MatDialog) { }
openTestModal() {
this.dialog.open(TestModalComponent, {
hasBackdrop: false
});
}
ngOnInit(): void { }
you can add a classes to change the dialog backgrounds or the backdrop by provide it as custom class in open config paramter
const dialogRef = this.dialog.open(DialogContentExampleDialog,{
backdropClass:'backdrop-bg-orange',
panelClass:'bg-blue'
});
style.scss
.bg-blue .mat-dialog-container{
background: lightblue;
}
.backdrop-bg-orange {
background: orange;
}
it 's important to put the classes in the global style file , it will not work if you add the classes to component template because angular update all component style to work just for the current component (ViewEncapsulation)
stackblitz demo 🚀

How is it possible to adjust a component's CSS based on a global CSS class-name in Angular?

We are using a class on the html-element to determine whether the user is in dark or light mode of the app.
<html class="dark-mode"></html>
This class is added using Renderer2 in a service that detects the selected setting of the user. This works fine so far.
Now, we have to adjust all the components to look good in the dark mode as well. But the problem is Angular's ViewEncapsulation.
What we thought would come in handy, is to simply update the SCSS of the component:
.headline {
color: black;
.dark-mode & {
color: white;
}
}
Or in plain CSS:
.headline {
color: black;
}
.dark-mode .headline {
color: white;
}
Now this doesn't work, because the class .dark-mode seems to be encapsulated, as expected.
How can we solve this problem?
:host-context provides access to css classes above the :host. By using :host-context you are able to check if any of the parents of the host have a specific css class and apply styling:
:host-context(.dark-mode) h2 {
color: white;
}
Documentation: https://angular.io/guide/component-styles#host-context

How to style a PrimeNG Chips?

I have a form in my application where I use the following code from Primafaces:
...other inputs...
<label for="workshopTags">Tags</label>
<p-chips
[(ngModel)]="values"
name="workshopTags"
id="workshopTags"
></p-chips>
I am able to display the Chip element correctly but I would like to style it putting its width to 100% and the height to 100px, but nothing seems to work to change those. This solution didn't work for me. I tried to set a styleClass or an inline style as the documentation suggest but they didn't work either. If I write inline:
style="width: 100%"
The following error is thrown:
Error: Cannot find a differ supporting object 'width: 100%;'
How can I make it work?
there are tow methos to style primeng component overwrite the original style or by create a custom style base on custome class
overwrite
add the style to global style.css or style.scss , this method for overwrite primeng component style without add extra class to the component.
.ui-chips {
display: inline-block
}
.ui-chips ul {
border:2px dashed green !important; /* 👈 I have use important */
}
.ui-chips > ul.ui-inputtext .ui-chips-token {
font-size: 14px;
background: green !important; /* 👈 I have use important */
border:1px solid #005555;
box-shadow: 0 0 25px #ccc;
}
stackblitz demo 🍏🍏
custome style
the method above will change the style of all p-chips component in the entier project , by this method you need to set styleClass property so you can create different style like the example here 👇 , but you need to set styleClass property for every component
<p-chips [(ngModel)]="values" styleClass="p-chips"></p-chips>
style.css
.p-chips.ui-chips {
/* border:1px solid #ff2200; */
display: inline-block
}
.p-chips.ui-chips ul {
border:2px dashed orange;
}
.p-chips.ui-chips > ul.ui-inputtext .ui-chips-token {
font-size: 14px;
background: orange;
border:1px solid #ff5555;
box-shadow: 0 0 25px #ccc;
}
stackblitz demo 🍊🍊
You can use ht /deep/ modifier ,add this inside your app.component.css and delete it from your style.css, and you don't need !important to force the style here, delete it. here is what you are looking for
p {
font-family: Lato;
}
/deep/ .p-chips > .ui-inputtext {
width: 100%;
height: 100px;
}
check it here https://stackblitz.com/edit/angular-primeng-startup-kmm7xw
You could try encapsulation: ViewEncapsulation.None in your override component decorator:
#Component({
selector: 'app-chip',
templateUrl: 'path-to-template where you use ui-chips',
styleUrls: ['path-to-styles where you could override ui-chips styles'],
encapsulation: ViewEncapsulation.None
})
export class AppChipComponent { }
To set the width to 100%, you have to use the style or styleClass attribute and set the css property display: block.
As an example using the styleClass attribute
<p-chips
[(ngModel)]="interests"
styleClass="block">
</p-chips>
Here I am using Prime Flex v3 with PrimeNg.

Angular 5 and :host pseudo class

I have been using the :host pseudo class in Angular 4 with no problems. We are converting to Angular 5, and now the style does not seem to be getting added to the host component. Is there a different way to add this now in the new version of Angular?
:host {
border: 10px solid black;
&.isLeft {
display: flex;
}
}
#media (max-width: 599px) {
:host {
display: flex;
}
}
The isLeft class is set up based on a parameter that is passed into the component from the parent component because this particular control can be displayed in 2 different formats, one of which is responsive. Inside the component, I have the following code:
#HostBinding('class.isLeft') isLeft: boolean;
#Input()
placement: string = 'responsive';
ngOnInit() {
this.isLeft = (this.placement.toLowerCase() === 'left');
}
I know there is a suggestion to use #HostBinding to do all of this. First of all, I'm not sure how that would work when I have to use media queries. Secondly, the only way to use #HostBinding with this would be to include styling inside the component, which goes against our company standards.
Thanks in advance!
You could use:
#HostBinding('style.border') styleborder = '10px solid black';
define isLeft in css, and put:
#HostBinding('class.isLeft') someField: boolean = true;
Code had included
encapsulation: ViewEncapsulation.None
Is working now that that is removed.

Resources