Angular 6 library shared stylesheets
Asked Answered
A

4

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
Alby answered 29/5, 2018 at 10:35 Comment(6)
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.Phenylamine
@Phenylamine I did, I initiated the project using this guide: medium.com/@tomsu/…Alby
I'm facing the exact same problem. Did you find a solution for this?Lustful
@BramW. no I have not, I've just added the imports in every file I want to use the commons in.Alby
github.com/angular/angular-cli/issues/10927 indicates that it is a limitation of ng-packagr and 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.Fumy
Is importing variables into each component scss not an option for you?Optician
S
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!

Susuable answered 26/7, 2019 at 11:42 Comment(11)
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 ?Roussillon
@Roussillon 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.Susuable
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.Roussillon
@Roussillon 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.Susuable
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 !Roussillon
Note: xcopy is windows only, but you can use copyfiles on Mac (or Windows): npmjs.com/package/copyfilesManofwar
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?Sped
@Sped 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..Susuable
@Xpleria: Ok. Inside the "lib", I added the line "entryFile": "src/public-api.ts" and ran the command ng build <library-project-name> --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?Sped
for your update- would you please add an example of how we can use the styles from the library in an angular project?Clos
@Sofia you could use @import '@my/library/path-to-file/file-name.scss'. I've also updated my answer. Cheers :)Susuable
L
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.

Landes answered 10/7, 2018 at 15:5 Comment(3)
@JeffyHouiser how can you bring theme to a project from libraryKillam
@IamStalker I'm sorry, I do not understand your questionLandes
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.Blasted
R
-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.

Roman answered 29/5, 2018 at 10:36 Comment(2)
"The specified command ("init") is invalid. For a list of available options, run "ng help"."Alby
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 projectRoman
M
-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?

Martinmas answered 11/6, 2019 at 19:2 Comment(1)
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.Skirting

© 2022 - 2024 — McMap. All rights reserved.