Customising CSS in SCRM Version 8.6.2

It’s the leads module - so already the SuiteCRM 8 front-end.
I’d like to add some small CSS adjustments in the leads list & detail view.
Can you point me to a specific piece of documentation with CSS details?
All I can find is scss from Suite7.

Impossible to customize CSS in Suite 8 modules?

What changes are you trying to add in the Leads module?

The leads module is using angular front-end in suitecrm 8.x

It could be possible to move it to legacy mode, but you will not have version 8 features.

I’d like to add CSS to SuiteCRM 8 in general - in a future proof way.
In SuiteCRM 7 there are a few hacks / posts on how to do this.
SuiteCRM 8 doesn’t support themes and no options for CSS files, as it seems?

For those who want some way of adding styles to angular fronted with easy option to switch back when there’s native way to do it then here’s how I did it.

Quick rundown:

  1. install webpack manifest plugin
  2. edit your modules webpack.config.js to generate manifest
  3. add one service to load manifest and another to load the stylesheet
  4. loads manifest and stylesheet when your module is required by suitecrm core

Positives:

  • upgrade safe
  • doesn’t change anything about the default way your extension is built
  • easy to revert/adapt when there’s a native way to do it
  • scss is automatically compiled to css when extension is built

Negatives:

  • styles are loaded asynchronously from the module itself

Step by step:

  1. install webpack manifest plugin
npm install webpack-manifest-plugin --save-dev
  1. edit your extensions webpack.config.js file at extensions/{extension name}/app/webpack.config.js and add additional plugin as seen below
...

const { WebpackManifestPlugin } = require('webpack-manifest-plugin');


module.exports = {
  ...

  plugins: [
    ...

    new WebpackManifestPlugin({
      fileName: 'manifest.json',
      publicPath: '/',
      generate: (_seed, files) => {
        const manifest = {};
        files.forEach(file => {
          if (['main.js', 'styles.css'].includes(file.name)) {
            manifest[file.name] = file.path;
          }
        });
        return manifest;
      },
    }),
  ],
};
  1. create two services for your app in extensions/{extension name}/app/src/services

extensions/{extension name}/app/src/services/manifest.service.ts

import { Injectable } from "@angular/core";

@Injectable({
  providedIn: "root",
})
export class ManifestService {
  async loadManifest(
    moduleName: string,
  ): Promise<Record<string, string> | void> {
    try {
      const response = await fetch(`/extensions/${moduleName}/manifest.json`);
      if (!response.ok) {
        throw new Error(`Failed to load manifest: ${response.statusText}`);
      }
      const manifest = await response.json();
      const parsedManifest = {};
      Object.entries(manifest).forEach(
        ([key, value]) =>
          (parsedManifest[key] = `/extensions/${moduleName}/${value}`),
      );
      return parsedManifest;
    } catch (error) {
      return console.error("Error loading manifest:", error);
    }
  }
}

extensions/{extension name}/app/src/services/styles.service.ts

import { Injectable, Renderer2, RendererFactory2 } from "@angular/core";

@Injectable({
  providedIn: "root",
})
export class StylesService {
  private renderer: Renderer2;

  constructor(rendererFactory: RendererFactory2) {
    this.renderer = rendererFactory.createRenderer(null, null);
  }

  loadStyle(href: string): void {
    const link = this.renderer.createElement("link");
    this.renderer.setAttribute(link, "rel", "stylesheet");
    this.renderer.setAttribute(link, "href", href);
    this.renderer.appendChild(document.head, link);
  }
}
  1. bootstrap it in your extensions/{extension name}/app/src/extension.module.ts and for loadManifest function pass your extension name as first param e.g. myExt, defaultExt or however your extension is called
...

import { StylesService } from "./services/styles.service";
import { ManifestService } from "./services/manifest.service";

@NgModule({
  ...
})
export class ExtensionModule {
  constructor(
    ...

    protected manifestService: ManifestService,
    protected stylesService: StylesService,
  ) {
    this.manifestService
      .loadManifest("{extension name}")
      .then((manifest) => this.stylesService.loadStyle(manifest["styles.css"]));
  }

  ...
}
  1. rebuild your extension and your extensions/{extension name}/app/src/styles.scss contents should be loaded when you open the page and updated each time you rebuild your extension
npm run build:extension {extension name}
2 Likes