Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a theme-able Angular Material NPM module?

Tags:

Is it possible to build an npm module that uses Angular Material and allows the components it defines to be styled by the consuming app's custom theme?

As an example, say I create an npm module that includes a component with the following template:

<button mat-raised-button color="accent">Click me!</button> 

Is it possible to import this component into two different apps, each of which defines a custom Angular Material theme, and have the button take on the "accent" color of the current app's theme?

Currently, I'm using ng-packagr to build Angular-friendly npm modules. However, as far as I can tell, this module includes a .scss compilation stage as part of the packaging process, which means any styles defined in the module lose their ability to be customized by a theme in an app that uses the module.

like image 417
Nathan Friend Avatar asked Jan 24 '18 16:01

Nathan Friend


People also ask

How do I create a custom material theme?

To do so, open the Settings and navigate to Appearance & Behavior → Material Theme UI → Custom Theme. Once you're done customizing your colors, you'll need to select Custom Theme or Light Custom Theme from the Theme Switcher to see your colors in action. Enjoy!

Can we customize angular material?

For any Angular Material component, you can safely define custom CSS for a component's host element that affect the positioning or layout of that component, such as margin , position , top , left , transform , and z-index .

How would you include a prebuilt theme in angular application without using the angular CLI?

You can find the pre-built theme files in the "prebuilt-themes" directory of Angular Material's npm package ( @angular/material/prebuilt-themes ). To include the pre-built theme in your application, add your chosen CSS file to the styles array of your project's angular. json file.


2 Answers

The essentials on how to do this are partly covered by the Angular Material Theming Your Own Components guide. In addition to theming your components, your library ("npm module") also needs to provide an entry point for all of its own styling in the same way that the Angular Material library does via its angular-material-theme() mixin. This is normally done by defining a mixin to define styles and call all component theming mixins, rather than creating a stylesheet file with style definitions. The mixin takes a theme as a parameter, and uses that theme to define all of its own styling as well as applying theming to any of its components. For example, your library might have its own _theme.scss file that any consuming application could import, which would define the mixin:

@import '~@angular/material/theming'; @import './components/my-component/_my-component-theme'; // component theming file with mixin @mixin library-theme($theme) {      @include angular-material-theme($theme); // angular material      @include my-component-theme($theme); // component theming      // define non-component styles } 

The consuming application would need to import your library's theming file, create its own theme, and then call your library's theming mixin.

There is obviously more detail to it, but those are the basics. For reference, I recommend you look at how Angular Material does its own theming via GitHub.

like image 188
G. Tranter Avatar answered Oct 03 '22 20:10

G. Tranter


Indeed, it is possible. The question just doesn't relate to Angular, it applies to any other framework out there.

Before that, you need to glace over the concept of dependency inversion. Say you are building your apps App1 and App2 and use an npm module lib which contains your reusable component. Your apps are built on top of lib meaning your apps are higher level module and lib is lower level module.

If you consider your lib is self-contained then ideally, it should perform same be it used in App1 or App2. But your requirements are - lib should have different behavior (styling in this case) as per the app. It means that you want the higher level module to make some decisions on how the lower level module should behave. So you are inverting dependency and that is dependency inversion principle. Read more about DIP on wikipedia.

Now to achieve DIP, you will need to expose some sort of port/extension from your library and for styling purpose, the simplest way is to expose SCSS mixins. But if you use any build tool like Webpack, rollup or ng-package, then they will always generate compiled distributions. You will have to write some extra node.js scripts to package your source code (SCSS here) along with the compiled code. These scripts will be executed during compilation/bundling step.

At my work, I am having similar requirements. There are some guidelines you should follow:

  1. Components from a library should work out of the box with some default styling.
  2. By default, a user of the library should not have to customize the component each time.
  3. Expose mixins from the components for styles.
  4. Within mixins, do not put any hardcoded preferences.
  5. When developing a themeable component, start with default. Override default CSS classes used by the component within your applications. Chalk out the commons customizations points/properties and then put it back into the next version of the library. Don't do upfront development within a library. It often results in rework.

We have an enterprise level apps. There are total 5 distinct apps and one privately published npm module that provides commons components to all these five apps. But there are some components which need to be styled differently which as I said, first override classes in our apps, and then once it feels generic enough to put into common module, we then put it in next release.

like image 43
Harshal Patil Avatar answered Oct 03 '22 22:10

Harshal Patil