Search code examples
cssangularangular-material

Semantic colors in an Angular Material component


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.


Solution

  • 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.