Reputation: 81
I have created a library in angular which is styled using tailwind. This is then been push to NPM and then imported into a new project, but the css is not getting applied. I have referenced the node-module path in my tailwind.config.ts:
content: [
"./src/**/*.{html,ts}",
'./node_modules/components-name/**/*.{html,js,ts}'
],
What am i missing?
Tailwind is working if i apply it directly to the new application, it just doesn't work with the imported library.
Upvotes: 8
Views: 9219
Reputation: 2487
It works for HTML classes aswell as @apply
.
Create another tailwind.config.js
in your library root, e.g. ./projects/ngx-lib/tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./projects/ngx-lib/src/**/*.{html,ts}'],
};
The problem is, that we can not scope all Tailwind base-styles out of the box, so we need to fix this.
Create projects/ngx-tib/src/theme/tailwind-setup.scss
and put in
@tailwind base;
@tailwind components;
@tailwind utilities;
Also create projects/ngx-tib/src/theme/tailwind.scss
.
In your lib.component.scss
, add:
ngx-lib {
@import "../theme/tailwind";
}
In your package json, add this script and always run it before building the library:
"build:lib": "npm run build:lib:tailwind && ng build ngx-lib",
"build:lib:tailwind": "tailwind -c projects/ngx-lib/tailwind.config.js -i projects/ngx-lib/src/theme/tailwind-setup.scss -o projects/ngx-lib/src/theme/tailwind.scss",
In the end, the SCSS compiler will do the prefixing for us.
If you are importing the library to your demo app during development, you will need to add the library paths to the root project's tailwind.config.js
:
content: [
'./src/**/*.{html,ts}',
'./projects/ngx-lib/src/**/*.{html,ts}'
],
Upvotes: 4
Reputation: 455
It's actually very easy, no need for precompiler hacks
Got this solution from the Tailwind docs, which suggest using the npx tailwind
build tool (instead ofnpx tailwindcss-cli
, like other solutions on the Internet suggest).
In your library folder, have the Tailwind config content
as if the origin is the base project folder:
module.exports = {
content: [
'./projects/my-lib/**/*.{html,ts,css,scss}',
'./**/*.{html,ts,css,scss}',
],
};
In step 3, a tailwind.scss
file will be generated into the my-lib/src/lib
folder, so (as suggested here) you have to include the tailwind.scss
file in your component:
styleUrls: ['../tailwind.scss']
.
(Careful with the path)
Run this command
npx tailwindcss@latest -c ./projects/my-lib/tailwind.config.js -o ./projects/my-lib/src/lib/tailwind.scss
before building the library. package.json
{
"scripts": {
"build:my-lib": "npx tailwindcss@latest -c ./projects/my-lib/tailwind.config.js -o ./projects/my-lib/src/lib/tailwind.scss & ng build my-lib"
}
}
Upvotes: 3
Reputation: 1139
If you expect all depender apps to utilize tailwind, you can use tailwind classes in your library HTML and have them configure a content
path of ./node_modules/my-lib/esm2020/**/*.mjs
.
It finds the inlined/escaped classes in the Ivy compiled files.
esm2020
to scope the scan.
@apply
in the library@apply
s are not resolved in precompiled library code as these files are not processed in that lifecycle.
As a workaround, you can pre-process your components to resolve @apply
styles before building the library.
tailwind.config.js
to use in the compilation
@tailwind components
or anything, we won't get any excess stylesprojects/my-lib/tailwind.config.js
module.exports = {
content: [
'./projects/my-lib/**/*.{html,ts,css,scss}',
],
};
Note the
content
path is still relative from project root as that's the context it's ran at
import { readFile, writeFile } from "fs";
import { sync } from 'glob';
import { exec } from 'child_process';
const libRoot = 'projects/my-lib/src/lib';
const tailwindConf = 'tailwind.config.js'; // may be apps/demo when using NX
const processedExt = '.precompiled.scss';
const styleRegex = /styleUrls:\s*\[([^\]]+)]/;
// Find all `.scss` files and tailwind process them
sync(`${libRoot}/**/*.component.scss`).forEach(file => {
const cssFile = file.replace(/\.scss$/, processedExt);
exec(`npx tailwind -c ${tailwindConf} -i ${file} -o ${cssFile}`, (err, stdout, stderr) => {
if (err) {
console.error(stderr);
throw err;
}
});
});
// .component.ts update
// Find all components with `styleUrls` and switch `.scss` extension to our precompiled file names
sync(`${libRoot}/**/*.component.ts`).forEach(file => {
readFile(file, (err, data) => {
if (err) throw err;
const content = data.toString();
const match = content.match(styleRegex);
if (match) {
const styleUrls = match[1]
.split(',')
.map(s => s.trim().replace('.scss', processedExt))
.join(', ');
writeFile(file, content.replace(styleRegex, `styleUrls: [${styleUrls}]`), (err) => {
if (err) throw err;
});
}
});
});
This should only be ran by your CI process and never committed. Also this could easily be switched to javascript instead of typescript
Other possible ways to do this (untested) without the .component.ts
update:
environment.prod.ts
's production: true
flag to decide the style file to use
styleUrls: [ environment.prod ? 'my.component.precompiled.scss' : 'my.component.scss' ],
package.json
"build:ci": "node --require ts-node/register projects/my-lib/src/precompile.ts && npm run build:my-lib"
--require ts-node/register
if converted to javascriptI use NX workspace, so I added a new target in the library's project.json
:
"ci": {
"executor": "nx:run-commands",
"options": {
"command": "node --require ts-node/register libs/my-lib/src/precompile.ts"
}
},
and added a the package.json
entry as:
"build": "nx run-many --all --target build",
"build:ci": "npx nx ci && npm run build",
allowing build
to still be used locally.
@apply
's resolved, all should flow wellIf you want applications to be able to utilize your library without them installing tailwind you could supply a stylesheet containing all the helper classes you used.
projects/my-lib/style.scss
@tailwind utilities;
postbuild
to your package.json
to produce the stylesheet, assuming you use npm run build
to build the library. "postbuild": "npx tailwind -c projects/my-lib/tailwind.config.js -i projects/my-lib/style.scss -o dist/my-lib/style.scss",
@import 'my-lib/style.scss'
Note tailwind does not compile SCSS into CSS - need to run through a SASS processor if you want to supply CSS.
Downside of this is all utility classes used in all components are produced, even if the depender app doesn't use them (same happens for projects using tailwind, so not so bad). Also the depender project may produce duplicate utility classes if using tailwind itself.
Plus side is your library doesn't require the depender to have tailwind.
Note that you still need the above process to resolve
@apply
's - this only gathers the utility classes used in the HTML
Upvotes: 8