Reputation: 11849
My Angular Material v18 application allows the user to set their theme but one component group, the "order control", has two semantic colors: blue for "buy"; and red for "sell" (similar to blue for "cold" and red for "hot" in a weather application) which are independent of the theme. Here's an HTML version that kinda does what it should followed by an Angular Material section which does not change color.
How can I get the Angular Material order control to change the semantic colors when BUY/SELL is selected, similarly to the HTML version?
Here's the HTML version:
<div class="order-control">
<div>
<span [class]="buyColor()" (click)="isSell=false">BUY</span>
<span [class]="sellColor()" (click)="isSell=true">SELL</span>
</div>
<div>
<label for="units">Units</label>
<input
[class]="getColor()"
type="range"
id="units"
name="units"
[min]="unitsMin"
[max]="unitsMax"
[(ngModel)]="units"/>
</div>
<div>
<label for="price">Price</label>
<input
[class]="getColor()"
type="range"
id="price"
name="price"
[min]="priceMin"
[max]="priceMax"
[(ngModel)]="price"/>
</div>
<div>
<button [class]="getColor()" type="button">Submit order</button>
</div>
</div>
And here's a similar version with Angular Material but the colors of the toggle, sliders, and submit button should change when selecting the order "side" (i.e., buy or sell):
<div class="order-control">
<div>
<mat-button-toggle-group name="side" [(ngModel)]="isSell">
<mat-button-toggle [value]="false">BUY</mat-button-toggle>
<mat-button-toggle [value]="true">SELL</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div>
<label for="units">Units</label>
<mat-slider
[max]="unitsMax"
[min]="unitsMin"
[step]="unitsStep"
discrete showTickMarks>
<input matSliderThumb [(ngModel)]="units" #unitsSlider />
</mat-slider>
</div>
<div>
<label for="price">Price</label>
<mat-slider
[max]="priceMax"
[min]="priceMin"
[step]="priceStep"
discrete showTickMarks>
<input matSliderThumb [(ngModel)]="price" #priceSlider />
</mat-slider>
</div>
<div>
<button mat-flat-button>Submit Order</button>
</div>
</div>
CSS
.order-control {
display: flex;
flex-direction: column;
justify-content: center;
.sell {
color: red;
accent-color: red;
}
.buy {
color: blue;
accent-color: blue;
}
}
TypeScript
@Component({
selector: 'app-root',
standalone: true,
imports: [ FormsModule, MatButtonModule, MatButtonToggleModule, MatSliderModule ],
templateUrl: './main.html',
styleUrl: './main.css',
})
export class App {
isSell: boolean = false;
units = 1;
unitsMin = 1;
unitsMax = 10;
unitsStep = 1;
price = 500;
priceMin = 0;
priceMax = 1000;
priceStep = 25;
buyColor() {
return this.isSell ? '' : 'buy';
}
sellColor() {
return this.isSell ? 'sell' : '';
}
getColor() {
return this.isSell ? 'sell' : 'buy';
}
}
Here's a working sample.
Upvotes: 2
Views: 117
Reputation: 11
maybe a more general solution is possible using the @material/material-color-utilities package once you have established the seed colors for the warm and cold themes use:
generateThemeFromSeed(color: string) {
const argb = argbFromHex(color);
const targetElement = document.documentElement;
// Get the theme from a hex color
const theme = themeFromSourceColor(argb);
// Apply theme to root element
applyTheme(theme, {
target: targetElement,
dark: true or false
brightnessSuffix: true,
});
const styles = targetElement.style;
for (const key in styles) {
if (Object.hasOwn(styles, key)) {
const propName = styles[key];
// color utilities generate variables with --md-sys- prefix, we need to change it to --sys
if (propName.indexOf('--md-sys') === 0) {
const sysPropName = '--sys' + propName.replace('--md-sys-color', '');
targetElement.style.setProperty(
sysPropName,
targetElement.style.getPropertyValue(propName)
);
}
}
}
}
Upvotes: 0
Reputation: 3531
I would advise against doing so, since it would break the design : when you implement a design system, there's no "independent component", it should all follow the design provided.
That being said, just apply your class conditionally ?
<mat-button-toggle [value]="true" [class.sell]="isSell">SELL</mat-button-toggle>
Upvotes: 0
Reputation: 597
To achieve your desired effect, add the following to your styles.scss
file:
@use '@angular/material' as mat;
@include mat.core();
$my-theme: mat.define-theme((
color: (
primary: mat.$violet-palette
)
));
html {
@include mat.all-component-themes($my-theme);
}
$blue-theme: mat.define-theme(
(
color: (
primary: mat.$blue-palette
)
)
);
$red-theme: mat.define-theme(
(
color: (
primary: mat.$red-palette
)
)
);
.blue {
@include mat.all-component-colors($blue-theme);
}
.red {
@include mat.all-component-colors($red-theme);
}
Then modify your order-control
div as follows:
<div class="order-control" [class]="isSell ? 'red' : 'blue'">
Good luck with your project.
Here's a PoC.
Upvotes: 2
Reputation: 379
You can try to create a custom theme for the specific components like so
@use '@angular/material' as mat;
@include mat.core();
$custom-palette: (
50: var(--Your-colors),
...
)
$custom-primary: mat.define-palette($custom-palette, 500)
$theme: mat.define-light-theme((
color: (
primary: $custom-primary,
accent: $custom-accent,
),
))
$buy: mat.define-palette($theme, 500)
@include mat-button-theme($buy)
.buy-theme {
@include mat-button-theme($buy);
}
Then add logic that assign CSS variables here --Your-colors
Upvotes: 1