Looking at the Angular Material documentation, they recommend using a -theme file per component to manage applying any theme-related styles to a specific class.
From my perspective, some downsides to this approach are:
quite verbose
splits up the style into two different locations
all your frontend devs need to understand how Angular Material colours work
makes it harder to change values (e.g. we might've been using mat-color($primary, 200) for border colours and now want to change it to mat-color($primary, 300). This will have been repeated all throughout the codebase.
Given a consistent design language, there will only be a subset of colors that will be used (e.g. 4 colors from the primary palette, 3 from the accent palette, a few different foreground / background colors, etc).
Given the above, doesn't it make more sense to have a _colors.scss that defines the exact colors using the theme rather than hoping developers extract the correct value from the theme each time?
e.g. maybe something like:
$clr-primary-default: mat-color($primary);
$clr-primary-contrast: mat-color($primary, default-contrast);
$clr-primary-light: mat-color($primary, lighter);
$clr-primary-dark: mat-color($primary, darker);
$clr-accent-default: mat-color($accent);
$clr-accent-light: mat-color($accent, lighter);
$clr-accent-dark: mat-color($accent, darker);
$clr-default-text: mat-color($foreground);
$clr-secondary-text: mat-color($foreground, secondary-text);
//etc
Then rather than creating a separate -theme file for each component that requires specific colors, I can simply import the colors.scss file and use the variables directly in the *.component.scss file.
Just wanting to validate that the above is sound and that I'm not missing anything obvious that's going to cause pain down the track?
The other tricky part is how to actually define these in a separate colors file efficiently given the file will need access to the theme data.
Why use #mixin?
Just wanting to validate that the above is sound and that I'm not missing anything obvious that's going to cause pain down the track?
The only thing, I can think of, is that you would miss the opportunity to use multiple themes in one application. With the approach from the Angular Material documentation, you would have a #mixin for each component, that you can #include multiple times with different $theme variables.
Example from https://medium.com/#tomastrajan/the-complete-guide-to-angular-material-themes-4d165a9d24d1:
.default-theme {
#include angular-material-theme($theme);
#include custom-component-theme($theme);
}
.light-theme {
#include angular-material-theme($light-theme);
#include custom-component-theme($light-theme);
}
This wouldn't work, if you import colors as scss-variables into your components and use it there.
Separate file for colors
The other tricky part is how to actually define these in a separate colors file efficiently given the file will need access to the theme data.
This is actually pretty straight forward: I have a separate file src/styles/_variables.scss that contains my custom colors as scss-variables and also the $theme variable, that I am using later in src/theme.scss.
#import '~#angular/material/theming';
// Theme configuration
$primary: mat-palette($mat-blue, 800, 500, 900);
$accent: mat-palette($mat-blue, A200, A100, A400);
$warn: mat-palette($mat-red);
$theme: mat-light-theme($primary, $accent, $warn);
// Custom colors
$custom-colors: (
custom-color-a: mat-color($mat-green, 700),
custom-color-b: mat-color($mat-red, 400),
);
$theme: map-merge($theme, (custom-colors: $custom-colors));
To import my _variables.scss inside a component, I have to add stylePreprocessorOptions to the angular.json file:
"styles": [
"src/styles.scss",
"src/theme.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
"src/styles"
]
},
Now I can import my variables in all scss-files of my components:
#import 'variables';
.custom-class-a {
background-color: map-get($custom-colors, custom-color-a);
color: map-get($custom-colors, custom-color-b);
}
Why do I use a sass-map and map-merge?
As you noticed, I collect my custom colors in the sass-map $custom-colors and merge them into my $theme variable. This way I could either use my custom colors by directly importing it into my components style sheet (as described above) or I could use them inside my components #mixin the way it is described in the Angular Material documentation.
#import '~#angular/material/theming';
#mixin custom-component-theme($theme) {
$custom-colors: map-get($theme, custom-colors);
.custom-class-a {
background-color: map-get($custom-colors, custom-color-a);
color: map-get($custom-colors, custom-color-b);
}
}
Maybe this combination is a way that your frontend devs could work with?
I have defined primary,accent and warn colors as css custom variables in the styles.css file like so:
#import "~#angular/material/theming";
#include mat-core();
$my-primary: mat-palette($mat-blue-grey);
$my-accent: mat-palette($mat-amber, A200, A100, A400);
$my-warn: mat-palette($mat-deep-orange);
$my-2-primary: mat-palette($mat-pink, 400, 200, 600);
$my-2-accent: mat-palette($mat-blue, A200, A100, A400);
$my-2-warn: mat-palette($mat-deep-orange, 500, 300, 700);
.dark-theme {
$my-theme-dark: mat-dark-theme($my-primary, $my-accent, $my-warn);
#include angular-material-theme($my-theme-dark);
$primary: mat-color($my-primary);
$accent: mat-color($my-accent);
$warn: mat-color($my-warn);
$fg_palette:map-get($my-theme-dark, foreground);
$bg_palette:map-get($my-theme-dark, background);
$fg:map-get($fg_palette, text);
$bg:map-get($bg_palette, background);
--primary: #{$primary};
--accent: #{$accent};
--warn: #{$warn};
--fg: #{$fg};
--bg: #{$bg};
}
.dark-theme-2 {
$my-2-theme-dark: mat-dark-theme($my-2-primary, $my-2-accent, $my-2-warn);
#include angular-material-theme($my-2-theme-dark);
$primary: mat-color($my-2-primary);
$accent: mat-color($my-2-accent);
$warn: mat-color($my-2-warn);
$fg_palette:map-get($my-2-theme-dark, foreground);
$bg_palette:map-get($my-2-theme-dark, background);
$fg:map-get($fg_palette, text);
$bg:map-get($bg_palette, background);
--primary: #{$primary};
--accent: #{$accent};
--warn: #{$warn};
--fg: #{$fg};
--bg: #{$bg};
}
And used these variables in any of my components like so:( in my-custom-component.scss)
.some-class {
color: var(--primary)
}
.another-class {
background-color: var(--bg)
}
.yet-another-class {
border-color: var(--accent)
}
By doing like this, i can change any value related to color in any component, because these variables are global (defined in styles.css)
As i change theme, these colors also change according to new theme's color
I am working on a project where I used the Material 2 Themes and I used this approach where I use the class name and add colors class globally.
This is what I did :
FileName: mytheme-sidemenu.scss:
// Import all the tools needed to customize the theme and extract parts of it
#import "~#angular/material/theming";
// Define a mixin that accepts a theme and outputs the color styles for the component.
#mixin mytheme-sidemenu($theme) {
// Extract whichever individual palettes you need from the theme.
$primary: map-get($theme, primary);
$accent: map-get(
$theme,
accent
); // Use mat-color to extract individual colors from a palette as necessary.
.col-primary {
color: mat-color($primary, 500) !important;
}
.col-accent {
color: mat-color($accent, 300) !important;
}
}
Here is my main theme file:
mytheme-theme.scss:
#import '~#angular/material/theming';
#import './variables/helper.scss';
#import './variables/spacemanager.scss';
#import './mytheme-sidemenu.scss';
// Primary theme
#include mat-core();
$mytheme-app-primary: mat-palette($mat-light-blue, 700, 600);
$mytheme-app-accent: mat-palette($mat-pink, A200, 900, A100);
$mytheme-app-warn: mat-palette($mat-deep-orange);
$mytheme-app-theme: mat-light-theme($mytheme-app-primary, $mytheme-app-accent, $mytheme-app-warn);
#include angular-material-theme($mytheme-app-theme);
// Secondary Theme
.mytheme-alt-theme {
$mytheme-alt-primary: mat-palette($mat-blue-grey, 500);
$mytheme-alt-accent: mat-palette($mat-pink, 500);
$mytheme-alt-warn: mat-palette($mat-deep-orange);
$mytheme-alt-theme: mat-light-theme($mytheme-alt-primary, $mytheme-alt-accent, $mytheme-alt-warn);
#include angular-material-theme($mytheme-alt-theme);
}
// Using the $theme variable from the pre-built theme you can call the theming function
#include mytheme-sidemenu($mytheme-app-theme);
and in app.module.ts update this :
export class AppModule {
constructor(
#Inject(OverlayContainer) private overlayContainer: OverlayContainer
) {
this.overlayContainer
.getContainerElement()
.classList.add("mytheme-alt-theme"); // this for double theme add to the root css class
}
}
Related
I trying to add more color to my angular material theme, I've added the success color in my style.scss by the map.deep-merge fucntion
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
#use '#angular/material' as material;
#use "sass:map";
#include material.core();
$custom-primary: material.define-palette(material.$light-blue-palette);
$custom-accent: material.define-palette(material.$blue-palette, A200, A100, A400);
$custom-warn: material.define-palette(material.$red-palette);
// extra Colors
$custom-success: material.define-palette(material.$green-palette);
$custom-danger: material.define-palette(material.$orange-palette);
$custom-theme: material.define-light-theme((
color: (
primary: $custom-primary,
accent: $custom-accent,
warn: $custom-warn,
)
));
$custom-theme: map.deep-merge(
$custom-theme,(
success: $custom-success,
danger: $custom-danger,
color:(
success: $custom-success,
danger: $custom-danger
)
)
);
#include material.all-component-themes($custom-theme);
Everything compiles correctly but when I try to apply the color to a button It looks like this
and don't have any idea why.
<button mat-raised-button color="success">Success</button>
<button mat-raised-button color="danger">Danger</button>
Curremtly I'm using "#angular/material": "^13.2.4",
I think only one step was missing: Adding .mat-success and .mat-danger CSS classes:
.mat-success {
background-color: material.get-color-from-palette($custom-success, 500);
color: material.get-color-from-palette($custom-success, 500-contrast);
}
.mat-danger {
background-color: material.get-color-from-palette($custom-danger, 500);
color: material.get-color-from-palette($custom-danger, 500-contrast);
}
I also removed the map.deep-merge, which was not necessary in this solution.
Complete theme.scss file:
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
#use "#angular/material" as material;
#include material.core();
$app-primary: material.define-palette(material.$purple-palette);
$app-accent: material.define-palette(material.$teal-palette, A200);
$app-warn: material.define-palette(material.$red-palette);
// extra Colors
$custom-success: material.define-palette(material.$green-palette);
$custom-danger: material.define-palette(material.$orange-palette);
$custom-theme: material.define-light-theme(
(
color: (
primary: $app-primary,
accent: $app-accent,
warn: $app-warn,
),
)
);
#include material.all-component-themes($custom-theme);
.mat-success {
background-color: material.get-color-from-palette($custom-success, 500);
color: material.get-color-from-palette($custom-success, 500-contrast);
}
.mat-danger {
background-color: material.get-color-from-palette($custom-danger, 500);
color: material.get-color-from-palette($custom-danger, 500-contrast);
}
Github Demo Link (unfortunately Stacklitz has problems with the custom theme)
if you used angular-CLI while building the project:
as per the official docs -
If you are using the Angular CLI, support for compiling Sass to CSS is
built-in; you only have to add a new entry to the "styles" list in
angular-cli.json pointing to the theme file (e.g.,
custom-theme.scss).
So make sure your theme is included in your angular.json and the necessary imports exist in the app.module.ts, and now you can import your theme in every file like this: #import YOUR_FILE_NAME After editing the angular.json file, you will need to run ng-serve again.
if you are not using angular-CLI:
as per the official docs -
If you are not using the Angular CLI, you can include a prebuilt theme
via a element in your index.html.
so first make sure that all the imports are correct
if neither helped:
add another import in each of the component files where mat* components are used
import { MatButtonModule } from "#angular/material/button";
Remember that when you add that import, your IDE will tell you (by greying it out) that it's not needed and can be removed.
if even that didn't help
As per latest angular material documentation you have to import modules from its respective package instead of #angular/material/
So change from:
import { MatToolbarModule , MatButtonModule, MatIconModule} from '#angular/material';
to:
import { MatToolbarModule } from '#angular/material/toolbar';
import { MatButtonModule } from '#angular/material/button';
import { MatIconModule } from '#angular/material/icon';
last resort
try building a minimal reproduction of an app using the same theme you built and post it here or try debuging it yourself (you'll be surprised by how much it can help)
and in any case after each change, if nothing happens, run ng-serve
I'm trying to create custom theming palette with unique names and colors in Angular application project with using Angular Material, or maybe even without using components library.
According Angular Material first I followed Theming your Angular Material app and created my-theme.scss in src folder directory like this:
#import "~#angular/material/theming";
#include mat-core();
I tried with name from _theming.scss:
$my-theme-my-name1: mat-palette($mat-green, 300, 400, 500, 700);
$my-theme-my-name2: mat-palette($mat-amber, 300, 400, 500, 700);
then:
$my-theme: mat-palette(
$my-theme-my-name1,
$my-theme-my-name2
);
#include angular-material-theme($my-theme);
then I add path src/my-theme.scss in angular.json like this:
"styles": [
{
"input": "src/my-theme.scss"
},
"src/styles.scss"
],
and use name in <button my-theme-my-name1>My button</button> but button does not changes color.
styles.scss includes #import '~#angular/material/theming'; by default with installation of library, maybe I have to change something only in styles.scss without creating my-theme.scss and using from _theming.scss, I'm not sure exactly how, but something like:
$my-theme-my-name1: my-palette(#fff, 300, 400, 500, 700);
because it looks like, that I'm doing something wrong way, colors does not changed and ng serve throws error with adding src/my-theme.scss to angular.json:
An unhandled exception occurred: ENOENT: no such file or directory,
lstat '..\src\my-theme.scss' See "..\ng-hZt0fz\angular-errors.log" for
further details.
[error] Error: ENOENT: no such file or directory, lstat '...\src\my-theme.scss'
at Object.realpathSync (fs.js:1728:7)
at ...\node_modules\#angular-devkit\build-angular\src\webpack\configs\styles.js:44:35
at Array.forEach (<anonymous>)
at Object.getStylesConfig (...\node_modules\#angular-devkit\build-angular\src\webpack\configs\styles.js:35:76)
at ...\node_modules\#angular-devkit\build-angular\src\dev-server\index.js:91:23
at generateWebpackConfig (...\node_modules\#angular-devkit\build-angular\src\utils\webpack-browser-config.js:45:49)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async generateBrowserWebpackConfigFromContext (...\node_modules\#angular-devkit\build-angular\src\utils\webpack-browser-config.js:136:20)
at async Object.generateI18nBrowserWebpackConfigFromContext (...\node_modules\#angular-devkit\build-angular\src\utils\webpack-browser-config.js:77:20)
at async setup (...\node_modules\#angular-devkit\build-angular\src\dev-server\index.js:87:47)
This $my-theme-my-name1 is a sass variable, adding this directly to the button as an attribute will not style your button. (You cannot reference sass variables in HTML or TS).
Here is how you can style your own components.
Create a custom selector (id or class) for your button and add theme colors to it.
Example
/*...necessary imports*/
$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent: mat-palette($mat-pink, A200, A100, A400);
$candy-app-warn: mat-palette($mat-red);
$candy-app-theme: mat-light-theme((
color: (
primary: $candy-app-primary,
accent: $candy-app-accent,
warn: $candy-app-warn,
)
));
#include angular-material-theme($candy-app-theme); //This will make sure to style material components
.my-custom-btn {
background-color: mat-color($candy-app-primary);
color: mat-color($candy-app-primary, default-contrast);
}
Now add the class my-custom-btn to the button <button class="my-custom-btn">My button</button>
The above is just an example, It is recommended to create your own mixin for styling your own components.
And coming to the error, this clearly means that the file my-theme.scss is deleted, removed or renamed.
I am using a package called Buefy which is a Vue.js wrapper for the Bulma CSS framework library. Buefy puts an attribute/property on its template components called type (e.g., type="is-warning"). As per the Buefy documentation, the term "is-warning" is pre-defined according to the $colors variable set within SCSS:
So I followed these instructions from Buefy to be able to customize the the $colors map, but what I would like to do, however, is keep the initial SCSS defined in those instructions within a single base.scss file, and then extend with my own customized SCSS file.
I have included the base.scss file in my Vue project with the following snippet in the vue.config.js file:
module.exports = {
css: {
loaderOptions: {
sass: {
prependData: `
#import "#/assets/scss/base.scss";
`
}
}
}
}
So my question is, how can I extend the $colors map in the base.scss with my own names and values?
This is a snippet from base.scss, where the $colors map is defined:
```css
$colors: (
"white": ($white, $black),
"black": ($black, $white),
"light": ($light, $light-invert),
"dark": ($dark, $dark-invert),
"primary": ($primary, $primary-invert),
"info": ($info, $info-invert),
"success": ($success, $success-invert),
"warning": ($warning, $warning-invert),
"danger": ($danger, $danger-invert),
"twitter": ($twitter, $twitter-invert)
);
```
So, again, my question is how can I extend the $colors map in a different file outside of base.scss (perhaps in App.vue) to leave the original untouched? I didn't see any solution shown in the scss docs.
Your custom color values would go in a variable (later merged into $colors) called $custom-colors, and it's actually described in the docs on "derived variables".
And to customize it:
// Import Bulma's core
#import "~bulma/sass/utilities/_all";
// Set your colors
$custom-colors: (
"facebook": ($blue, $white),
"linkedin": ($dark-blue, $white)
// etc.
);
// Setup $colors to use as bulma classes (e.g. 'is-twitter')
$colors: (
"white": ($white, $black),
"black": ($black, $white),
"light": ($light, $light-invert),
"dark": ($dark, $dark-invert),
"primary": ($primary, $primary-invert),
"info": ($info, $info-invert),
"success": ($success, $success-invert),
"warning": ($warning, $warning-invert),
"danger": ($danger, $danger-invert),
"twitter": ($twitter, $twitter-invert)
);
// Import Bulma and Buefy styles
#import "~bulma";
#import "~buefy/src/scss/buefy";
It's internally using a custom function called mergeColorMaps.
"#angular/material": "^6.2.1"
Got quite stuck with a very minor issue.
Can't import any standard material color in any of my app's css/scss files, including styles.scss
example:
$app-panel-green: mat-color($mat-green);
gives this error in any scss file:
ERROR in ./src/styles.scss (./node_modules/raw-loader!./node_modules/postcss-loader/lib??embedded!./node_modules/sass-loader/lib/loader.js??ref--14-3!./src/styles.scss)
Module build failed:
$app-panel-green: mat-color($mat-green);
^
Undefined variable: "$mat-green".
in /**********/src/styles.scss (line 1, column 29)
styles.scss consits only of (except previous):
#import "~#angular/material/prebuilt-themes/deeppurple-amber.css";
/*#import './theme.scss';*/
body { margin: 0; }
and pre-built theme works just fine.
I don't even need a custom theme, just want to use material color palette.
If you want to use one of default palettes, you can do it like this:
#import '~#angular/material/theming'; // Pay attention on the path
#include mat-core();
$candy-app-primary: mat-palette($mat-deep-orange);
$candy-app-accent: mat-palette($mat-deep-orange, A200, A100, A400);
$candy-app-warn: mat-palette($mat-red);
$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);
#include angular-material-theme($candy-app-theme);
And don't forget to include the file with your theming to .angular-cli.json to styles section.
I am using angular 5 material and i created a theme.scss as below
theme.scss
#import '~#angular/material/theming';
#include mat-core();
$custom-primary: mat-palette($mat-deep-purple,600);
$custom-accent: mat-palette($mat-lime, 100);
$custom-warn: mat-palette($mat-red);
$custom-theme: mat-light-theme($custom-primary, $custom-accent, $custom-warn);
#include angular-material-theme($custom-theme);
// ALTERNATIVE THEME
$alt-primary: mat-palette($mat-yellow);
$alt-accent: mat-palette($mat-grey, 200);
$alt-theme: mat-dark-theme($alt-primary, $alt-accent);
.alternative {
#include angular-material-theme($alt-theme);
}
I have my default styles.scss as below
style.scss
#import "~#angular/material/prebuilt-themes/indigo-pink.css";
.fa-icon-nav {
color: #507889;
font-size: x-large;
}
The color is currently hardcoded in the fa-icon-nav. I want it to use primary color from the currently selected theme. Please advise how this would work if possible? Happy to hear if this is totally the wrong way to do it and how it should be done.
EDIT:
ADDED ONLINE DEMO
I am working on a project where I used the Material 2 Themes and I used this approach where I use the class name and add colors class globally.
This is what I did :
FileName: mytheme-sidemenu.scss:
// Import all the tools needed to customize the theme and extract parts of it
#import "~#angular/material/theming";
// Define a mixin that accepts a theme and outputs the color styles for the component.
#mixin mytheme-sidemenu($theme) {
// Extract whichever individual palettes you need from the theme.
$primary: map-get($theme, primary);
$accent: map-get(
$theme,
accent
); // Use mat-color to extract individual colors from a palette as necessary.
.col-primary {
color: mat-color($primary, 500) !important;
}
.col-accent {
color: mat-color($accent, 300) !important;
}
}
Here is my main theme file: mytheme-theme.scss:
#import '~#angular/material/theming';
#import './variables/helper.scss';
#import './variables/spacemanager.scss';
#import './mytheme-sidemenu.scss';
// Primary theme
#include mat-core();
$mytheme-app-primary: mat-palette($mat-light-blue, 700, 600);
$mytheme-app-accent: mat-palette($mat-pink, A200, 900, A100);
$mytheme-app-warn: mat-palette($mat-deep-orange);
$mytheme-app-theme: mat-light-theme($mytheme-app-primary, $mytheme-app-accent, $mytheme-app-warn);
#include angular-material-theme($mytheme-app-theme);
// Secondary Theme
.mytheme-alt-theme {
$mytheme-alt-primary: mat-palette($mat-blue-grey, 500);
$mytheme-alt-accent: mat-palette($mat-pink, 500);
$mytheme-alt-warn: mat-palette($mat-deep-orange);
$mytheme-alt-theme: mat-light-theme($mytheme-alt-primary, $mytheme-alt-accent, $mytheme-alt-warn);
#include angular-material-theme($mytheme-alt-theme);
}
// Using the $theme variable from the pre-built theme you can call the theming function
#include mytheme-sidemenu($mytheme-app-theme);
and in app.module.ts update this :
export class AppModule {
constructor(
#Inject(OverlayContainer) private overlayContainer: OverlayContainer
) {
this.overlayContainer
.getContainerElement()
.classList.add("mytheme-alt-theme"); // this for double theme add to the root css class
}
}
This is already asked and I just copy paste my answer from here
EDIT :
As per your need, you can achieve this:
I have my default styles.scss as below style.scss that you need to change to _theme-color.scss
.fa-icon-nav {
color: mat-color($primary, 500) !important; // 500, 600 , 700 check material color pallet for more info
// You can use this too
// color: mat-color($alt-primary, 500) !important;
font-size: x-large;
}
or you can use this material color library from here