Highnoon
Highnoon

Reputation: 94

Extending SCSS Styles in an Angular Dashboard with Plugin Architecture

I am developing a Dashboard in Angular with a Plugin Architecture to keep it easily extensible. The dashboard consists of pre-defined visualizations like scatter plots. I also want to allow adding visualizations from external sources, i.e., an angular component that is capable of drawing 3D plots. I have successfully implemented the Plugin Architecture on the Angular and Typescript side. However, I'm facing challenges in making the SCSS styles easily extensible and ensuring a seamless look and feel when integrating visualizations from external sources.

Specifically, I want each component to have its own styles, such as font color and size. Additionally, I want to define a global-styles.scss file where I can overwrite these styles. When styles are defined in global-styles.scss, they should overwrite the local styles.

Currently, I have come up with the following (working) approach:

/* global-styles.scss */
:root {
  --header-color: hotpink;
}
/* scatter-plot.scss */
:host {
  --default-header-color: blue;
}

.header {
  color: var(--header-color, var(--default-header-color));
}

While this approach seems to work, it involves lots of repetition because I always need to use var(--XY, var(--default-XY)) for each custom property usage. So I'm wondering whether there's a cleaner and more efficient way to achieve the desired behavior? I have attempted to directly overwrite the custom properties, but I couldn't get it working as the "outer CSS" would need to overwrite the "inner CSS" (i.e., global-styles.scss should overwrite scatter-plot.scss).

EDIT

Ideally, it should also support theming (light and dark mode). Thus, I guess it would be easier to stick to CSS custom properties rather than SCSS, because they can be easily overwritten at runtime.

Upvotes: 3

Views: 1017

Answers (4)

Lincoln Alves
Lincoln Alves

Reputation: 654

There's one more option you could play with: style encapsulation. I see two possibilities:

  • Go full on ViewEncapsulation.None (at least for the customizable components)

It's more risky, since it could lead to unintentional styles overrides, especially with more people working on the project. I see this approach taken more on libraries.

  • Separate customizable tokens from components encapsulated styles

I think this approach could work well for your case! Separate customizable CSS variables from your components scss files. You could even keep them on the same folders if the variables are closelly related to each component and import these files on a main file, which could also contain global theming variables. These global variables could be used as initial default values for the component ones, creating a hierarchy...the possibilities are endless!

You could import the tokens stylesheet at the start of your global styles file, so variables overrides done after would work properly!

Hope this helps a little! Cheers!

Upvotes: 0

Eliseo
Eliseo

Reputation: 58019

You can try change the angular.json to change the styles

    "styles": [
          "src/styles.css",
          {
            "input":"src/css/global.css",
            "bundleName": "global"
          }
        ],

See the docs

As Angular create an index with the two styles

<style="style.***.css">
<style="global.***.css">

The only is "global" should override the style.css

To override you need your global.css looks like, e.g.

//my-app is the selector
my-app.custom{
  --background-color: #212a2e,
  --text-color: #F7F8F8,
  --link-color: red
}
//hello is the selector
hello.custom{
  --text-color:yellow;
}

your component like

@Component({
  selector: 'my-app',
  ...
  host: {class: 'custom'}
})

Or use

<hello class="custom" ...></hello>

NOTE: You can also include manually the global.css in the .html and, in this way, you can simply edit the global.css without to create again the app.

a stackbliz

Upvotes: 0

Eliseo
Eliseo

Reputation: 58019

You can change the .css variables using javascript.

Imagine a .json like

{
  "--background-color": "#212a2e",
  "--text-color": "#F7F8F8",
  "--link-color": "red"
}

You can to have a service that change the css variables based in this file

@Injectable({
  providedIn: 'root',
})
export class CssService {
  json: any;
  constructor(private httpCient: HttpClient) {}
  setCss(el: ElementRef) {
    const obs = !this.json
      ? this.httpCient
          .get('/assets/css.json')
          .pipe(tap((res) => (this.json = res)))
      : of(this.json);

    return obs.pipe(
      tap((res: any) => {
        Object.keys(res).forEach((key) => {
          if (window.getComputedStyle(el.nativeElement).getPropertyValue(key))
            el.nativeElement.style.setProperty(key, res[key]);
        });
      }),
      map((_) => true)
    );
   }
 }

Now in each component you can in constructor inject as public

constructor(public cssService: CssService, public el:ElementRef) {}

And put all under a div

<div *ngIf="cssService.setCss(el)|async">
  ...
</div>

stackblitz

Upvotes: 0

Ankit
Ankit

Reputation: 102

You can utilize CSS custom properties. By organizing your styles and leveraging cascading nature of CSS, you can achieve the desired behavior without the repetition of var(--XY, var(--default-XY)) for each custom property usage.

Here's an improved approach that builds upon your existing setup:

/* global-styles.scss */
:root {
--header-color: hotpink;
}

/* scatter-plot.scss */
:host {
--header-color: blue;
}

.header {
color: var(--header-color);
}

By using the var(--header-color) directly in the .header class, the pattern for each usage and rest will be taken care of with the help of CSS Cascading.

Regarding theming (light and dark mode), you can create different sets of custom properties for each theme, and swap them out dynamically. For example:

/* global-styles.scss */
:root {
--header-color-light: hotpink;
--header-color-dark: #333;
}

/* scatter-plot.scss */
:host {
--header-color: var(--header-color-light);
}

To switch to the dark theme, you can programmatically update the values of the custom properties to their corresponding dark mode values.

With this setup, you can easily extend and override styles in individual components while maintaining a seamless look and feel across your dashboard.

Try this and let me know.

Upvotes: 0

Related Questions