Dynamically generating CSS classes at runtime in Angular 7 - css

I have an Angular 7 application where I need to dynamically generate CSS classes in my component at runtime.
First off: I know about [ngStyle] and [ngClass]! I need to do some stuff using pseudo-selectors which I can't generate using these.
Now, the naive approach: I'll just use Angular to template my CSS!
<style *ngFor="let class of classes">
.{{class.prefix}}-target {
border: 1px solid maroon;
text-align: center;
}
.{{class.prefix}}-control:hover ~ .{{class.prefix}}-target {
background: red;
}
</style>
The problems with this approach:
VS Code complains that I'm putting Angular templating inside a CSS class.
Angular doesn't interpolate {{class.prefix}} in the final resulting HTML- it just literally puts {{class.prefix}} there.
Is there a way to do this in Angular, either with a library that's meant for this or an approach I can use? Thanks in advance!

Depending upon how dynamic you need the component to be...
You could declare the prefix (or dynamically generate it) outside of the component class. Then use a JavaScript template string to interpret the styles before they get processed by the #Component() decorator's metadata.
Like so:
const classPrefix = 'bar';
#Component({
selector: 'app-foo',
template: `
<!-- Your HTML template -->
`,
styles: [`
.${classPrefix}-target {
border: 1px solid maroon;
text-align: center;
}
.${classPrefix}-control:hover ~ ${classPrefix}-target {
background: red;
}
`]
})
export class FooComponent {
/* . . . */
}

Related

Can Lit or Web Components know attribute of parent 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>).

React Component SCSS available for all Components

I just started learning ReactJS and I made a Project with .scss
For some reason when I add a style in a .scss file that style also changes other components' styles as well.
example:
I add a li style in the Home.scss, but it will change the style of the Footer component's li too. I didn't import it into the Footer.js or anything.
Does anyone know what is the reason why does it do it, and what is the solution?
Adding a className per component won't solve your problem, it will work as expected until you have any nested component.
Because if you add
#component-name {
li {
...
}
}
The CSS will be applied to any component inside of that component too.
To limit your CSS to a component in react, you have a few options :
CSS Modules
Create React App supports CSS Modules out of the box (as of version 2)
It works with SCSS too (YourComponent.module.scss)
YourComponent.js:
import styles from './YourComponent.module.css'
export const YourComponent () => {
<ul>
<li className={styles.yourLi}>
</ul>
}
YourComponent.module.scss:
.yourLi {
color: blue;
}
CSS-in-JS
With this method, as the name suggests, you can declare your CSS within your JS.
There are multiple libraries to implement this.
Styled-Components
Here is an example with styled components which is the one that seems to be the most used as of today:
const YourLi = styled.li`
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
`
render(
<div>
<ul>
<YourLi>
Your styled li
</YourLi>
</ul>
</div>
)
Add a class footer in the first div of footer component
sass allows nested defining of classes like
.footer{
li{
}
}
using that can help.
since it doesn't matter where you import scss in react. styles are imported globally by default.

Angular 7 SCSS Doesn't Keep CSS Custom Properties

I'm trying to use CSS Custom properties in my Angular 7 app but am running into issues where building down to css gets rid of the property that is set to a CSS variable.
SCSS
:root {
--my-test-var: red;
}
.testclass123 {
height: 150px;
background: var(--my-test-var);
}
Builds down to:
:root {
--my-test-var: red;
}
.testclass123 {
height: 150px;
}
If I use a fallback options like var(--my-test-var, purple); then .testclass123 will also have property background: purple;
My Angular version is 7.2.7 and Angular Material 7.3.7
If you use SCSS as global style you can define like this the color.
This is the best approach.
$product: red;
And then only you call when you need like this.
background-color: $product;

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.

How to create 'smart' nested rule for css using webpack and vue

I don't know how to better name this topic
but idea is the following. I want to show different color for a component depends on a parent class.
for this project I use webpack, vue, vue-loader, sass.
I have a sass file this file contents all settings for pages what color should use for specific page
$colors: ".page-home" blue, ".page-about" green;
#each $i in $colors {
$page: nth($i, 1);
$color: nth($i, 2);
#{$page} .component_1, .component_2, .component_n {
color: $color;
}
}
I have a component is written as vue component
#import "colors";
.compoent_1 {
border:1px solid black
}
A issue is I have a lot of components and it very difficult to support the colors file in consistency. When I want to add a new component or remove an old one I always have to go to this file and edit it is annoying me
So how I see the solution is create a main file.
.page-home:blue;
.page-about: green;
I'd like write components in the following style
.component {
border:1px solid black;
color: $PAGE_COLOR;
}
and this code should generate
.page-home .component_1, .component_2, .component_n {
color: blue;
}
.page-about .component_1, .component_2, .component_n {
color: green;
}
thats all. thanks for any suggestion

Resources