Angular Material Theme Styles not applying in Migrated Angular Project - css

I am trying to migrate an older Angular v9 project to the latest version (as of writing) v13. I have successfully migrated everything except the styles, particularly the Angular Material theme styles, which do not appear to apply at all.
Here is a comparison of the same Login page:
Angular v9
Angular v13
Apologies for not being able to post the whole page, but these are the relevant parts. Ignoring the increased height and input fields, which I understand need to be done differently in HTML, the buttons look completely wrong. Inspecting the items reveals that the basic styling for the shape and form are being applied from Angular Material, but seemingly the custom theme styles do not work at all. When investigating further, I can see that it appears that the custom theme styles, defined in a SCSS file, are being generated, but the CSS class names are being mangled:
material.module.scss:
#use "#angular/material" as mat;
#import "environment.scss";
#import "./material.variables";
#import "../Scss/variables";
// Include the common styles for Angular Material. We include this here so that you only have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
$typography: mat.define-typography-config($font-family: $primary-font-face);
#include mat.core($typography);
// Theme Configuration
$primary-colour: $primary;
$primary-lighter-colour: #bde8f9;
$primary-darker-colour: #1498e1;
$accent-colour: $green;
$accent-lighter-colour: $green-pastel;
$accent-darker-colour: $green-dark;
$warn-colour: $red;
$warn-lighter-colour: $red-pastel;
$warn-darker-colour: $red-dark;
#mixin mat-body-variables($name, $main-colour, $lighter-colour, $darker-colour, $text-colour) {
--#{$name}-color: #{$main-colour};
--#{$name}-lighter-color: #{$lighter-colour};
--#{$name}-darker-color: #{$darker-colour};
--text-#{$name}-color: #{$text-colour};
--text-#{$name}-lighter-color: #{$text-colour};
--text-#{$name}-darker-color: #{$text-colour};
}
body {
#include mat-body-variables(primary, $primary-colour, $primary-lighter-colour, $primary-darker-colour, $light-text);
#include mat-body-variables(accent, $accent-colour, $accent-lighter-colour, $accent-darker-colour, $light-text);
#include mat-body-variables(warn, $warn-colour, $warn-lighter-colour, $warn-darker-colour, $light-text);
}
#function get-material-palette($main-colour, $light-colour, $dark-colour, $text-colour) {
$mat: (
50: $light-colour,
500: $main-colour,
900: $dark-colour,
text: $text-colour,
contrast: (
50: $text-colour,
500: $text-colour,
900: $text-colour,
)
);
#return mat.define-palette($mat, 500, 50, 900, text);
}
$theme-primary: get-material-palette($primary-colour, $primary-lighter-colour, $primary-darker-colour, $light-text);
$theme-accent: get-material-palette($accent-colour, $accent-lighter-colour, $accent-darker-colour, $light-text);
$theme-warn: get-material-palette($warn-colour, $warn-lighter-colour, $warn-darker-colour, $light-text);
$theme: mat.define-dark-theme((
color: (
primary: $theme-primary,
accent: $theme-accent,
warn: $theme-warn
)
));
#include mat.all-component-themes($theme);
$lightTheme: mat.define-light-theme((
color: (
primary: $theme-primary,
accent: $theme-accent,
warn: $theme-warn
)
));
.light-theme {
#include mat.all-component-colors($lightTheme);
}
.mat-raised-button,
.mat-form-field {
width: 100%;
}
mat-tab-group,
.mat-tab-body-wrapper {
height: 100%;
}
button,
.mat-flat-button,
.mat-raised-button {
transition: background-color 0.2s, color 0.2s;
}
Seeming output in Chrome Inspector of generated styles.css (partial for brevity):
And scrolling further up I can see the custom colours interspersed in the mangled CSS classes, so I assume these are the theme styles.
My question is then, how do I correctly import & apply these styles to the project?
The theme stylesheet is currently included in the angular.json file, as recommended by the Theming Angular Material Guide (emphasis mine):
To include the emitted styles in your application, add your theme file to the styles array of your project's angular.json file.
although I have tried including it in our main stylesheet for this project, to no avail.
I have also tried changing the optimisation settings in angular.json, as well as messing around with supposed environment variables that control the mangling of the CSS names, but neither of those worked, nor particularly appeal as solutions.
Fwiw, what does seem to work is when I include one of the prebuilt theme stylesheets into the styles array, then they apply correctly. I have also tried creating separate projects outside of this one to see if it was my environment messing with things, however with both the default Angular Material themes, and our custom theme, the theming worked correctly.
So please, can anyone help with this frustrating issue?
Thank you, Will

Related

Avoid duplicate styles in Angular Material theme

I have a styles.theme.scss that looks like the following.
#media (prefers-color-scheme: dark) {
#include example-theme($dark-theme);
}
#media (prefers-color-scheme: light) {
#include example-theme($light-theme);
}
[data-theme="dark-theme"] {
#include example-theme($dark-theme);
}
[data-theme="light-theme"] {
#include example-theme($light-theme);
}
The goal is to use the prefers-color-scheme if that is configured by the user agent, but override it if the user has configured it on the website.
The current SCSS causes the following warning, however:
WARNING: The same color styles are generated multiple times. Read more about how style duplication can be avoided in a dedicated guide. https://github.com/angular/components/blob/master/guides/duplicate-theming-styles.md
node_modules/#angular/material/_theming.scss 1648:7 -mat-check-duplicate-theme-styles()
node_modules/#angular/material/_theming.scss 7010:3 angular-material-theme()
stdin 29:3 example-theme()
stdin 57:3 root stylesheet
I've checked the provided documentation, but it doesn't appear to cover this case, and I'm unsure on how to better structure this to avoid duplicating the styles.
The only solution I think would work is to detect the preference with JavaScript, and then set the data-theme attribute if a theme isn't configured in the application.
Is there a better solution than this?
What I've tried:
To see if I could string a media query and selector together like [data-theme="dark-theme"], #media (prefers-color-scheme: dark), which I don't believe is possible.
If I can use SASS #if and #else to check if the data-theme selectors match any elements, else include the #media queries.
You should include mat.all-component-colors instead of mat.all-component-themes
Example: below is wrong
// Generates styles for all systems configured in the theme. In this case, color styles
// and default density styles are generated. Density is in themes by default.
#include mat.all-component-themes($light-theme);
.dark-theme {
// Generates styles for all systems configured in the theme. In this case, color styles
// and the default density styles are generated. **Note** that this is a problem because it
// means that density styles are generated *again*, even though only the color should change.
#include mat.all-component-themes($dark-theme);
}
Use this instead:
#use '#angular/material' as mat;
...
#include mat.all-component-themes($light-theme);
.dark-theme {
// This mixin only generates the color styles now.
#include mat.all-component-colors($dark-theme);
}
For more information, Official Docs
I also find my self fighting same issue today.
The problem with your styling is you have two theme light and dark and in
#include angular-material-theme('Your-Theme');
you have to provide a theme ( light or dark).
so you should overwrite only one of theme because other is already included
so overwrite media query dark if you provided light one or vice versa.
here is my code sample
#import "~#angular/material/theming";
#include mat-core();
$kyc-web-primary: mat-palette($mat-pink);
$kyc-web-accent: mat-palette($mat-pink, A200, A100, A400);
$kyc-web-warn: mat-palette($mat-red);
$kyc-web-theme-light: mat-light-theme(
(
color: (
primary: $kyc-web-primary,
accent: $kyc-web-accent,
warn: $kyc-web-warn,
),
)
);
$kyc-web-theme-dark: mat-dark-theme(
(
color: (
primary: $kyc-web-primary,
accent: $kyc-web-accent,
warn: $kyc-web-warn,
),
)
);
#include angular-material-theme($kyc-web-theme-dark);
#media (prefers-color-scheme: light) {
#include angular-material-color($kyc-web-theme-light);
}
mat.$theme-ignore-duplication-warnings: true;

Dynamically override Bootstrap CSS variables in React?

So what I have is a React project with Bootstrap.css loaded. I'd like to somehow override the variables, so for instance I have a bunch of buttons like
<button className="btn btn-primary">Hello</button>
Which basically 'inherit' the color from:
:root {
--primary: somecolor;
}
Is there a way to somehow override this? I've tried passing it in as inline style to components, like <Component style={{"--primary" : "red"}} /> which will override the :root { --primary }, but the button colors will remain the same. What's the easiest way to do this, considering I'm supporting dynamic colors, so I can't create a few CSS files, and it would be good if I didn't have to rewrite every single button I have to be a styled-component that minds props!
There's not really an easy way to do this. You could generate the CSS for the "custom" primary colors in SASS, and then add a "root" primary color class to the component like this...
SASS to generate "primary" color Bootstrap CSS
/* import the necessary Bootstrap files */
#import "bootstrap";
#mixin build-primary-classes($color) {
/* background classes */
#include bg-variant(".bg-primary", $color);
/* btn classes */
.btn-primary {
#include button-variant($color, $color);
}
.btn-outline-primary {
#include button-outline-variant($color);
}
/* text- classes */
#include text-emphasis-variant(".text-primary", $color);
/* badge classes */
.badge-primary {
#include badge-variant($color);
}
/* borders */
.border-primary {
border-color: $color !important;
}
}
$customprimarycolors: (
"purple": purple,
"red": red,
"orange": orange
);
#each $colorName, $color in $customprimarycolors {
.#{$colorName} {
#include build-primary-classes($color);
}
}
JS
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className={this.props.className}>Hello <button className="btn btn-primary">Button</button></div>
);
}
}
ReactDOM.render(<Hello className="red" />, document.getElementById('root'));
Codeply demo: https://codeply.com/go/R4X5x8taiH
Not sure if this will work, but an idea...
Could you edit your bootstrap.css file like this
:root { --primary: unset; }
That way you wouldn't have to overwrite any bootstrap styles?
HONESTLY ... I am not sure if it is a good idea to try to overwrite in CSS a few part of the classes bootstrap builds on a color var like $primary but let's the rest of the classes build on the same var as it is.
That's the way trouble raises up ...
An IMHO better way would be to do it the way Bootstrap provides it:
Changing in SASS the basic var $primary to the new wanted color in Bootstrap the color will change and the classes are there. Or adding a new color $additional-color and adding the color to map $theme-color and all the additional classes are build up on the fly ... Just have a look to the docs ... it is much more easier than it seems to be on the first look:
https://getbootstrap.com/docs/5.0/customize/color/
So yes: in SASS theming is possible just setting/adding the color vars.
But if in a project it is not possible to use SASS or the direct setup is not wanted for some reasons ... there are many free easy to use theming tools in the web which do the job for you. Than you are able to import a clean and consistend Bootstrap CSS with your wanted colors. Because that it is as easy Bootstrap is as successful (what not means I/you need to like it). As Bootstrap 5 still is Beta here an example for BS4 ...
http://pikock.github.io/bootstrap-magic/app/index.html#!/editor
NOTE: only changing/overwriting the CSS color vars in CSS file is not enough as the colors in the classes are hard coded to the original hex colors. You indeed would have to overwrite the original classes which leads to doubled code structures.

What is best fit to use for theming variables or mixins in sass

I am creating a UI library in which I want to provide the mechanism to theme all the UI components like button, cards, slider and all. I am confused between variables and mixins.
One way is to provide the no. of variables that user can update and based on that variables component classes will be derived. The same concept is used in the materialzecss library. And user will use like
//variables that are used to create component css classes
$primary : "blue";
$btn-primary :"green";
//then include the ui library
#import "_ui-variables";
#import "ui-library";
_ui-variables.scss
$primary : "red" !default;
$btn-primary: $primary !default;
// and other variables
and the _btn.scss will be like
.btn {
// other rule sets
color:$btn-primary;
}
Other way could be to use mixins. There will be a theme file for every component that will contain the theme mixin for that component and at the library level, there will be theme mixin that will include all the mixin of the individual component. As the angular-material has done
_btn.scss
#import "_btn-theme.scss";
.btn {
// some rules
}
_btn-theme.scss
#mixin btn-theme($theme) {
// if user has added the btn-primary then use btn-primary otherwise primary
#if map-has-key($theme,btn-primary) {
$btn-primary : map-get($theme,primary);
} #else {
$btn-primary : map-get($theme,primary);
}
.btn {
color:$btn-primary;
}
}
and the ui-library.scss
#import "_btn.scss";
#import "_card.scss";
#mixin ui-theme($theme) {
#include btn-theme($theme);
#include card-theme($theme); // include all component theme
}
and the consumer will call this as
consumer-theme.scss
#import "ui-library";
$theme :(primary:"blue",accent:"yellow");
#include ui-theme($theme);
What are the pros and cons of these approaches? Is there any other way to do this?
If you can use CSS custom properties (CSS variables) that would be really easy. You would just need to add a class to the body change your all your variables at once. So you just need a default theme and then just some classes changing your theme.
I have a small example in one of my project, if you click on "invert theme" it will change the page theme to invert: https://vinceumo.github.io/atomic-bulldog-style-guide-demo/styleguide/section-organisms.html#kssref-organisms-accessibility-settings
The issue with CSS custom properties is that not every brother support it yet :/
https://caniuse.com/#feat=css-variables
Otherwise, I would highly recommend using sass maps. It is easier to maintain when you have few themes, and you can quickly generate your components using #each loop
For example, if you want to generate background color classes:
$color-themes: (
primary:
(
base: #4c5c8c,
dark: darken(#4c5c8c, 15%),
light: lighten(#4c5c8c, 15%),
transparent: transparentize(#4c5c8c, 0.5),
contrast: #ffffff
),
secondary:
(
base: #212529,
dark: darken(#212529, 15%),
light: lighten(#212529, 15%),
transparent: transparentize(#212529, 0.5),
contrast: #ffffff
)
}
#each $name, $theme in $color-themes {
.has-bg-#{$name} {
background-color: map-get($name, base);
color: map-get($name, contrast);
}
}
So here we will get two new classes .has-bg-primary, .has-bg-secondary
If you add new entries to your map it will automatically generate new classes :)
I have created a Scss boilerplate using CSS custom properties (This one can be disabled) with Sass variables. It is optimized for themes creation. Most components are linked to variables (using map). Check it out https://github.com/vinceumo/atomic-bulldog
Variables are going to be your initial best bet.
Creating a theme story isn't something you should rush through, but rather take the time to integrate solid, well thought variables for a base set of colors. After that, you can extend them with things like lighten(), darken(), and other tools built into SASS.
Then, use that base set of variables to establish component specific variables to scale the theme story as needed.

Building separate Stylesheets for different themes with webpack

Right now we are using a scss mixin ( https://github.com/zellwk/themify ) to provide different themes for our react components in our app.
It works great, but produces rather long selector chains in the compiled css file.
While we do have to provide the ability to change themes during runtime ( which is no hassle with the current solution, we only have to switch one classname on the body element ) the default usecase is, that the theme does not change.
So we thought about reducing the complexity and filesize of our stylesheets by splitting them up in separate files.
Example:
themes.scss:
$themes: (
red: (
mainColor: #aa3939,
secondaryColor: #D46A6A
),
blue: (
mainColor: #2e4272,
secondaryColor: #4f628e
)
);
button.sccs
#import 'themes.scss';
.button {
#include themify($themes) {
color: themed('secondaryColor');
background-color: themed('mainColor');
}}
}
This becomes:
.theme-red .button {
color: #D46A6A;
background-color: #aa3939;
}
.theme-blue .button {
color: #4f628e;
background-color: #2e4272;
}
Now I want this to become:
theme-red.css:
.button {
color: #D46A6A;
background-color: #aa3939;
}
theme-blue.css:
.button {
color: #4f628e;
background-color: #2e4272;
}
We are not depenent on the themify mixin, we could change that to any kind of solution one could make work with webpack. Any hint in the right direction would be appreciated! :)
#ManuKaracho, I tried to use the similar approach with the help of these two tutorials and faced the same issue as you are facing.
Sass Theming: The Neverending Story
Theming Web Apps with SASS
The CSS is generated in a single file instead of two separate files. I wanted to generate two separate CSS files just like you. I did some R&D about the issue and finally figured out how to do this.
First of all you need to break your themes.scss file into two separate files as shown below.
_theme-red-variables.scss
$themes: (
red: (
mainColor: #aa3939,
secondaryColor: #D46A6A
)
);
_theme-blue-variables.scss
$themes: (
blue: (
mainColor: #2e4272,
secondaryColor: #4f628e
)
);
Next you need to make some changes in your button.scss file. Simply remove the #import statement from the top because we will import theme specific variables into their own separate files as shown below
button.scss
.button {
#include themify($themes) {
color: themed('secondaryColor');
background-color: themed('mainColor');
}
}
Next you need to create two separate theme files. In these files, you need to import theme specific variables file, mixin file and your button.scss file
theme-red.scss
// Import red theme variables file
#import 'theme-red-variables';
// Import mixins file, where you have defined themify and themed mixins
#import 'mixins';
// Import button.scss file in this theme file
#import 'button';
theme-blue.scss
// Import blue theme variables file
#import 'theme-blue-variables';
// Import mixins file, where you have defined themify and themed mixins
#import 'mixins';
// Import button.scss file in this theme file
#import 'button';
Two separate files will be generated using the above technique
theme-red.css
.button {
color: #D46A6A;
background-color: #aa3939;
}
theme-blue.css
.button {
color: #4f628e;
background-color: #2e4272;
}
I hope I have explained the solution well and it will help you to resolve your problem.

How to set custom font for foundation button

I am trying to set a custom font-family for the button in foundation 6 with no luck I am running foundation 6 with sass and believe they have removed the $button-font-family variable that was in previous versions. What is the best solution to achieve this without the variable?
Ideally I would like to apply a text-transform on the button text as well which is also not a button variable in the _setting.sass foundation file. As you may have gathered I am very new to foundation and sass so any help would be greatly appreciated.
It doesn't look like a font-family is specified in scss/components/_button.scss and that's where button syles are defined. The $body-font-family is set in both scss/settings/_settings.scss as well as scss/_global.scss (if not already defined in _settings.scss). So, verify that you have updated the value of $body-font-family there.
Aside from that, if you are using foundation sass semantically (i.e. mixins), you should be able to use the button mixin like so:
#include button( false, #ebebeb, #a5a5a5, black, solid );
..and then set the font-family property after the mixin like you normally would.
----Update: Based on follow up question/comment----
Here's an example main sass file for one of the foundation sites I'm working on now.
// Author copy of Foundation settings/_settings.scss
#import "settings";
// Author copy of Foundation _globals.scss
#import "global";
// Import from Foundation
#import "path/to/foundation-sites/scss/grid/grid";
#import "path/to/foundation-sites/scss/typography/typography";
#import "path/to/foundation-sites/scss/components/button";
// Include Foundation classes/styles that we want to use
#include foundation-global-styles();
#include foundation-typography();
//
// Author Variables
//
$color-map : ( 'blue': #40578a, 'blue-tint': #7e8ba8, 'blue-shade': #364a75, 'dark': #3d3e41, 'silver': #bdc0c6, 'gold': #ab883c );
$blue : map-get($color-map, 'blue');
$blue-tint : map-get($color-map, 'blue-tint');
$blue-shade: map-get($color-map, 'blue-shade');
$dark : map-get($color-map, 'dark');
$silver : map-get($color-map, 'silver');
$gold : map-get($color-map, 'gold');
//
// Author Components
//
#import "common";
#import "btn";
#import "view";
#import "cover";
#import "header";
#import "hero";
#import "navbar";
#import "main";
#import "page";
#import "intro";
#import "services";
I'll go through this section by section. It's pretty simple and once you have a project set up this way, you'll love it.
// Author copy of Foundation settings/_settings.scss
Here, we are importing a copy of the Foundation _settings.scss file and modifying it for our needs. Don't mess with the original _settings file that ships with Foundation. Doing so will make it more difficult to update the library moving forward.
// Author copy of Foundation _globals.scss
Same concept as the _settings file.
// Import from Foundation
These imports are the only parts of the Foundation framework that I want to use in this case. These component files include the mixins used to generate these components. This allows us to use the mixins semantically in our code.
// Include Foundation classes/styles that we want to use
And this will output the pre-defined classes/styles that we want to use from foundation. Typically, you will at least want to include global and typography styles.
// Author Variables
This is where I put my custom variables (not related to Foundation). You could put this in another partial if you want - just a preference.
// Author components
These are the project's custom components which may or may not utilize Foundation's mixins. The point is, we can if we want to now that we have included the components/mixins we want to use.
So, in your case, you could simply do this:
// Foundation settings
#import "settings";
// Foundation globals
#import "global";
// Import from Foundation
#import "../path/to/foundation-sites/scss/components/button";
//
// Author Variables
//
$special-button-font: Georgia, serif;
.special-button {
#include button(false, #ebebeb, #a5a5a5, black, solid);
font-family: $special-button-font;
&.tiny { font-size: map-get($button-sizes, tiny); }
&.small { font-size: map-get($button-sizes, small); }
&.large { font-size: map-get($button-sizes, large); }
&.expanded { #include button-expand; }
}

Resources