61

I was trying to add global styles in the same way like in angular app, but it totally does not work.

My libraries' name is example-lib, so I added styles.css to /projects/example-lib/. I added styles in main angular.json file:

...
"example-lib": {
  "root": "projects/example-lib",
  "sourceRoot": "projects/example-lib/src",
  "projectType": "library",
  "prefix": "ngx",
  "architect": {
    "build": {
      "builder": "@angular-devkit/build-ng-packagr:build",
      "options": {
        "tsConfig": "projects/example-lib/tsconfig.lib.json",
        "project": "projects/example-lib/ng-package.json",
        "styles": [
          "projects/example-lib/styles.css" <!-- HERE 
        ],
      },
...

But when I tried build library using command:

ng build example-lib

I got error:

  Schema validation failed with the following errors:
  Data path "" should NOT have additional properties(styles)

I guess that is the other way to add global styles in separate library. Anyone can help me?

Alireza Ahmadi
  • 8,579
  • 5
  • 15
  • 42
Jaroslaw K.
  • 5,224
  • 2
  • 39
  • 65

5 Answers5

39

I have a workaround for this. Just create the root component of your library without view encapsulation and all its styles will be then global.

my-library.component.ts

import { Component, OnInit, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'lib-my-library',
  templateUrl: './my-library.component.html',
  styleUrls: ['./my-library.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class MyLibraryComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

my-library.component.html

<!-- html content -->

my-library.component.scss

@import './styles/core.scss';

Now your my-library.component.scss and core.scss are global

styles/core.scss

body {
    background: #333;
}

core.scss is optional, I just like to keep the root files clean.


Update: In case you want your mixins and variables too, then follow this answer.

Xpleria
  • 5,472
  • 5
  • 52
  • 66
  • How is this supposed to work? Do I have to wrap every library component in a `lib-my-library`? – Adam M Thompson Jun 10 '19 at 16:24
  • 1
    No, the styles are global. Meaning, they apply to all elements, regardless of which component they belong to. The styles are not encapsulated because of this line `encapsulation: ViewEncapsulation.None`. – Xpleria Jun 11 '19 at 11:32
  • Thanks @NeilPatrao - worked like a charm. Do you know if there is a better way now or is your workaround with empty component still the cleanest solution out there? – codeepic Apr 14 '20 at 12:27
  • @NeilPatrao Ok - I did as you described above and it works like a charm for Storybook but now that I want to use global styles from the library in the app that consumes the library, I need to put somewhere - ideally in app.component.html template the component and it is really hacky. I'd rather import the .scss with library global styles into the styles.scss file in the app, but the library styles are not exposed through library public -api – codeepic Apr 17 '20 at 13:54
  • 1
    @codeepic hi, checkout this answer https://stackoverflow.com/a/57219374/1393400 it has exactly the solution you're looking for. – Xpleria Apr 18 '20 at 14:04
  • Ok - I was already looking into your answer you pointed me to. You are most welcome for figuring this shit out. I guess, I will have to add the copy .scss step in the build like you suggest. It is really weird that the Angular team doesn't support global styles for libraries out of the box without devs having to resort to workarounds. – codeepic Apr 18 '20 at 22:10
  • 3
    @NeilPatrao - just checking the info on https://angular.io/guide/creating-libraries official library docs and it looks like starting with version 9 of ng-packagr you can configure the tool to automatically copy the assets. More on: https://angular.io/guide/creating-libraries#managing-assets-in-a-library – codeepic Apr 19 '20 at 20:54
  • @codeepic thanks for pointing it out, I've updated the other answer – Xpleria Apr 20 '20 at 08:48
  • this is a brilliant solution :) – Datum Geek Sep 25 '20 at 16:56
13

As @codeepic already pointed out, there is currently a standard solution.

In ng-package.json add

"assets": ["./styles/**/*.css"]

The provided paths should be the paths to your files. At the same time, they will be the paths inside your /dist folder.
On build, the files will be copied to /dist. Users of your library will be able to add them to their global styles as follows.

/* styles.css */
@import url('node_modules/<your-library-name>/styles/<file-name>');

This way you can copy any type of files.

P.S. When used with CSS, do not forget that you can create an index.css file that can be imported just like node_modules/<your-library-name>/styles.

Eugene P.
  • 1,517
  • 12
  • 16
  • Its also helpful to note that you need to create the folder `assets` where your styles will be. – Jnr Feb 21 '22 at 10:24
  • very good, work fine here to me, thanks you to your help. – Hoiama Rodrigues Mar 05 '22 at 11:26
  • That sort of worked for me, but I couldn't find my library's folder under `node_modules`. I could find it under `dist`, though. Should I be concerned this approach won't be reliable? Right now my structure looks like this: `workspace/projects/myLib/src/assets/styles/core.scss` and `workspace/projects/myApp/src/styles.scss` where I import from the lib – Guilherme Taffarel Bergamin Jul 18 '22 at 22:14
4

From Compiling css in new Angular 6 libraries:

  1. install some devDependencies in our library in order to bundle the css:

    • ng-packagr
    • scss-bundle
    • ts-node
  2. Create css-bundle.ts:

    import { relative } from 'path';
    import { Bundler } from 'scss-bundle';
    import { writeFile } from 'fs-extra';
    
    /** Bundles all SCSS files into a single file */
    async function bundleScss() {
      const { found, bundledContent, imports } = await new Bundler()
        .Bundle('./src/_theme.scss', ['./src/**/*.scss']);
    
      if (imports) {
        const cwd = process.cwd();
    
        const filesNotFound = imports
          .filter(x => !x.found)
          .map(x => relative(cwd, x.filePath));
    
        if (filesNotFound.length) {
          console.error(`SCSS imports failed \n\n${filesNotFound.join('\n - ')}\n`);
          throw new Error('One or more SCSS imports failed');
        }
      }
    
      if (found) {
        await writeFile('./dist/_theme.scss', bundledContent);
      }
    }
    
    bundleScss();
    
  3. Add _theme.scss inside the /src directory of the library that actually contains and imports all the css that we want to bundle.

  4. Add postbuild npm script to run the css-bundle.ts

  5. Include it in the styles tag in your Application in the angular.json

yazantahhan
  • 2,950
  • 1
  • 13
  • 16
  • 1
    Could you explain what include? > Include it in the styles tag in your Application in the angular.json – POV Jan 09 '20 at 21:47
  • @OPV open angular.json and go to the styles property (should be inside the build > options properties) and add it as an array item. You can check here for more info: https://www.truecodex.com/course/angular-6/how-to-include-external-css-and-js-file-in-angular-6-angular-7 – yazantahhan Jan 12 '20 at 08:53
  • `css-bundle.ts` links are dead. – Episodex Apr 01 '21 at 10:31
  • 1
    Thanks @Episodex for mentioning this. Updated the answer with the content. – yazantahhan Apr 01 '21 at 11:16
3

From this issue solution

Install cpx and scss-bundle as Dev dependencies to your package.json. Then add the following entries in your package.json "scripts" property:

"scripts": {
  ...
  "build-mylib": "ng build mylib && npm run build-mylib-styles && npm run cp-mylib-assets",
  "build-mylib-styles": "cpx \"./projects/mylib/src/lib/style/**/*\" \"./dist/mylib/style\" && scss-bundle -e ./projects/mylib/src/lib/style/_style.scss -d ./dist/mylib/style/_styles.scss",
  "cp-mylib-assets": "cpx \"./src/assets/**/*\" \"./dist/mylib/assets\"",
  ...
}

Replace "mylib" with your real library name and then just run in your terminal build-mylib. That would compile your scss assets to your dist folder.

You use this global styles in your actual Angular project just import them in your angular.json file within your project settings:

"styles": [
  "src/styles.scss",
  "dist/my-shiny-library/_theme.scss"
],

(use dist if your project is in the same workspace, or node_moduled if its an imported library)

Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Daniel Llano
  • 11,568
  • 1
  • 9
  • 7
2

1- be sure you are putting your styles inside the library

example:

projects/your-lib-name/assets/styles.css

2- then in your ng-package.json (in the lib for sure) put the assets rule

    {
      "$schema": ...  , 
      "dest": ...   , 
>     "assets": [
>         "./assets/*"
>       ],
      "lib": ...
    }

3- in your application, you can use this asset

  "styles": [
              "../your-lib-name/assets/styles.css"
            ]

this is a tutorial

Datz
  • 3,156
  • 3
  • 22
  • 50