31

How can you setup a index.scss and import global stylesheets for variables, mixins, etc, to an angular 6 library?

Angular CLI generates a lib with a root component & component scss, but the styles added or imported to the root component are not available to children components. Which makes sense by default to encapsulate the styles, but I just can't find any information or examples on how to set this up yet.

The angular.json "styles": [...] paths that can be used for this with "projectType": "application", don't seem to work with "projectType": "library" either.

Thanks in advance for your help!


UPDATE: My project was initiated using angular cli v6.0.5, following this guide: https://medium.com/@tomsu/how-to-build-a-library-for-angular-apps-4f9b38b0ed11

TL;DR for the guide:

ng new my-app --style=scss
ng generate library my-library --prefix ml

This is the file structure angular 6 generates:

    my-app
      projects/
        my-library/
          src/
            lib/
              shared/..
              widgets/..
              my-library.component.ts
              my-library.module.ts
            sass/
              _variables.scss
              styles.scss // <<< This is where I want to `@import 'variables';`, and for it to be available in all the components of the "my-library" project.
            public_api.ts
      src/
        app/
          app.module.ts // << imports projects/my-library/lib/my-library.module as "my-library".
        main.ts
        index.scss
        index.html
      README.md

Package Versions:

    Angular CLI: 6.0.5
    Node: 10.2.1
    OS: darwin x64
    Angular: 6.0.3
    ... animations, common, compiler, compiler-cli, core, forms
    ... http, language-service, platform-browser
    ... platform-browser-dynamic, router

    Package                            Version
    ------------------------------------------------------------
    @angular-devkit/architect          0.6.5
    @angular-devkit/build-angular      0.6.5
    @angular-devkit/build-ng-packagr   0.6.5
    @angular-devkit/build-optimizer    0.6.5
    @angular-devkit/core               0.6.5
    @angular-devkit/schematics         0.6.5
    @angular/cli                       6.0.5
    @ngtools/json-schema               1.1.0
    @ngtools/webpack                   6.0.5
    @schematics/angular                0.6.5
    @schematics/update                 0.6.5
    ng-packagr                         3.0.0
    rxjs                               6.2.0
    typescript                         2.7.2
    webpack                            4.8.3
xaunlopez
  • 439
  • 1
  • 4
  • 13
  • It looks like you did not create your project with @angular/cli, if it's a new project I highly recommend you to use it. – Ploppy May 29 '18 at 11:02
  • @Ploppy I did, I initiated the project using this guide: https://medium.com/@tomsu/how-to-build-a-library-for-angular-apps-4f9b38b0ed11 – xaunlopez May 29 '18 at 11:56
  • I'm facing the exact same problem. Did you find a solution for this? – Bram W. Jun 05 '18 at 13:55
  • @BramW. no I have not, I've just added the imports in every file I want to use the commons in. – xaunlopez Jun 11 '18 at 09:06
  • 2
    https://github.com/angular/angular-cli/issues/10927 indicates that it is a limitation of ng-packagr and https://github.com/angular/angular-cli/issues/10869 specifies a gulp workaround. Not thrilled with having to introduce gulp so another (non-ideal) option I have tried and am considering is using the "root" library component created by the Angular CLI to hold the global styles for the Library. It would have an empty template but with `encapsulation: ViewEncapsulation.None` which would cause its styles to be added to global styles. Then include the (empty) template tag somewhere in the app. – Jim Barrett Jun 18 '18 at 23:53
  • Is importing variables into each component scss not an option for you? – JayChase Aug 07 '19 at 04:54

4 Answers4

16

For global styles, I've answered it in this question.

Update

For ng-packgr versions 9.x and above

Copying assest to output folder is now directly supported as explained in this page

{
  "$schema": "./node_modules/ng-packagr/package.schema.json",
  "name": "@my/library",
  "version": "1.0.0",
  "ngPackage": {
    "assets": [
      "CHANGELOG.md",
      "./styles/**/*.theme.scss"
    ],
    "lib": {
      ...
    }
  }
}

So in your project you would use the styles by importing the files from the library in the following way:

@import '@my/library/path-to-file/file-name.scss'

or using explicit relative path (make sure to use as may ../ as required)

@import '../node_modules/@my/library/path-to-file/file-name.scss'

Old Answer

**For other versions**
  1. Create an index.scss file in your library's root folder. If you follow this guide from Angular, then your path will be my-project/projects/my-library/index.scss. This is also the folder where your package.json is.

So, index.scss will be the file with your variables and mixins

$grey: #222;
@mixin mymixin {
    background: #222;
}
  1. Include this in you library scss files using import
@import '../../index.scss';

or whatever relative path your component scss file is at.

  1. Now in order to have this file in your app project, copy it post build to the dist directory. To do this, edit your angular library's project's package.json file (NOT THE LIBRARY'S).
{
    "name": "my-project",
    "version": "1.0.0",
    "scripts": {
        "ng": "ng",
        "start": "ng serve",
        "build": "ng build && npm run copyScss",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e",
        "copyScss": "xcopy \"projects\my-library\index.scss\" \"dist\\my-library\\\""
    },

    ...
}
  1. Now, very important, DO NOT use ng build to build your library, instead use npm run build. This will automatically execute the copy command. Now the index.scss file is exported along with your library in the my-project/dist folder.

  2. Include the index.scss in your app project's scss files

// ~ stands for the node_modules folder
@import '~my-library/index.scss';

Now you have all your library mixins in all of the projects you installed your library.

Cheers!

PS Workarounds are not the most elegant solutions, but when nothing else works, they work around!

Xpleria
  • 5,472
  • 5
  • 52
  • 66
  • What if you want to expose a lib outside of the current project ? Like through `npm link` or published npm module, how can you make lib-mixin accessible from consummer's app ? – Mozgor Aug 26 '19 at 16:14
  • 1
    @Mozgor The solution already addresses it. Look at point 3. The `index.scss` gets copied to the `dist` folder and is shipped with the library. When you do `npm link`, everything in the dist folder including `index.scss` is copied to the library's folder under the `node_modules` folder. Then you simply import `index.scss` in the consumer app as explained in point 5. PS Do not skip point 4. or else it won't work. – Xpleria Aug 27 '19 at 07:15
  • Indeed, I misread and thought the copy was done in consummer app. Thanks for pointing it ! Can you please explain why `index.scss` should be a lib-root-level, and if it is needed when lib is a single component ? I'd rather just expose the theme's file in this case but I might be wrong. – Mozgor Aug 27 '19 at 09:57
  • @Mozgor It's not necessary that `index.scss` should be in the root directory. But it's easier to import when it's in the root directory. When you have a component, then the component's `scss` is compiled to `css`. You can still copy the `scss` to the dist folder (in the component's folder if you prefer). But then you have the css twice. That's why I suggested having an extra `index.scss` file, where you have only the common components i.e., mixins and variables. Then you don't have any redundant css. – Xpleria Aug 27 '19 at 12:57
  • Ok, got it. I created a dedicated file for theming, i.e containing only theme related mixin, so no css duplication in my case. I see your point thought. Thanks for insisting on your steps, really helped me ! – Mozgor Aug 27 '19 at 13:29
  • Note: xcopy is windows only, but you can use copyfiles on Mac (or Windows): https://www.npmjs.com/package/copyfiles – Uniphonic Feb 21 '20 at 20:50
  • In the first method for ng-packgr versions 9.x and above, in which file should I add the mentioned code? Is it in library's project's package.json? – suvenk Jan 07 '21 at 05:58
  • @suvenk yes `package.json` in library's project folder. That's how the `ng-packgr` will know which files to copy when packaging that corresponding library.. – Xpleria Jan 07 '21 at 07:40
  • 1
    @Xpleria: Ok. Inside the "lib", I added the line "entryFile": "src/public-api.ts" and ran the command ng build --prod. This creates dist folder inside the library project instead of creating at the "projects" folder level like it did previously. How I can make sure dist folder is created at ''projects" folder level? – suvenk Jan 10 '21 at 00:59
  • for your update- would you please add an example of how we can use the styles from the library in an angular project? – Sofía Jun 10 '22 at 13:40
  • 1
    @Sofia you could use `@import '@my/library/path-to-file/file-name.scss'`. I've also updated my answer. Cheers :) – Xpleria Jun 11 '22 at 11:05
1

Have you tried setting the encapsulation level of the component to none as part of the component metadata? Like this:

@Component({
  selector: 'app-component',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.None
})

The ViewEncapsulation levels are:

  • ViewEncapsulation.Emulated: The default, Angular emulates a Shadow DOM.
  • ViewEncapsulation.Native: Use the browser's own Shadow DOM.
  • ViewEncapsulation.None: Styles are Global

Check out the Angular Docs on ViewEncapsulation and I wrote a blog post on it.

JeffryHouser
  • 39,401
  • 4
  • 38
  • 59
  • @JeffyHouiser how can you bring theme to a project from library – hackp0int Feb 12 '19 at 09:23
  • @IamStalker I'm sorry, I do not understand your question – JeffryHouser Feb 12 '19 at 14:17
  • 4
    ViewEncapsulation.None should not be used b/c it adds global styles only after the component on which it's used is initialized. So you can get weird scenarios where some random part the app breaks only after you've gone to a totally other random part of the app. – Dr. Hilarius May 21 '19 at 20:46
-1

Run ng init on the project, so that it will initialize the project as an Angular CLI project.

Edit: My bad, it seems they removed init from the CLI. You can try ng new --src

It might overwrite some files, so try this on a copy of the project.

Xpleria
  • 5,472
  • 5
  • 52
  • 66
Arno4Jackie
  • 331
  • 1
  • 20
  • `"The specified command ("init") is invalid. For a list of available options, run "ng help"."` – xaunlopez May 29 '18 at 11:58
  • my bad, it seems they removed init from the cli you can try ng new --src it might overwrite so files so try this on a copy of the project – Arno4Jackie May 30 '18 at 07:24
-1

I think people may be missing the fact that while encapsulation options exist, global styles are output in style tags and can cascade throughout your project.

Here is my setup:

enter image description here

  1. My global styles: styles.scss is output in style tags. The rules target all matching classes and elements as expected. Even within components.

  2. Component styles are encapsulated. That is their value prop. They overwrite globals and keeps component-specific styles with the component family.

  3. @includes such as variables.scss, mixins.scss etc. are included explicitly at the top of any component .scss files when needed.

@import '../../../../scss/includes/variables';

What else would we need?

Ben Racicot
  • 5,332
  • 12
  • 66
  • 130
  • The global styles.scss is provided for Angular Applications, but it does not seem to be the case for Angular Libraries, which is what OP is trying to define a global style for. And there unfortunately only seems to be workarounds to do this. – The Once-ler Jun 16 '20 at 09:52