post-css not finding paths from node_modules - css

I currently have a Angular project which I am looking to purge the css using purgecss.
I have got everything working but when I import node_modules it struggles as it cannot find the paths which are located in the node_modules folder.
I have the current app.scss file
#import "#fortawesome/fontawesome-pro/scss/fontawesome";
#import "#fortawesome/fontawesome-pro/scss/regular";
#import "./_buttons";
The buttons class is actually called _buttons.scss but for some reason the postcss does not pick this up so I have to define the _ although I know it can be imported without.
So that is the first issue which I would like to fix if possible but the second is that when importing font awesome, it finds the font awesome package but it cannot find the file variables after I looked into the package I can see that there is no relative path and it is just variables. As this is a package is there a way to mitigate this issue within webpack to stop this from happening and the build from failing?
Here is my webpack.config.js
const purgecss = require("#fullhuman/postcss-purgecss");
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
loader: "postcss-loader",
options: {
modules: true,
importLoaders: 1,
ident: "postcss",
syntax: "postcss-scss",
plugins: () => [
require("postcss-import"),
require("autoprefixer"),
purgecss({
content: ["./**/*.html"],
whitelistPatterns: [/^cdk-|mat-/],
defaultExtractor: content =>
content.match(/[\w-/:]+(?<!:)/g) || []
})
]
}
}
]
}
};
I've tried setting importLoaders: 1 which didn't seem to make a difference at all.
How can I get postcss to run from the files root directory? Even without the ./ which is used in the fontawesome package and also the postcss recognising the scss file without having to explicit prefix everything with _
Edit (font awesome error):
fontawesome.scss
#import 'variables';
#import 'mixins';
#import 'core';
#import 'larger';
#import 'fixed-width';
#import 'list';
#import 'bordered-pulled';
#import 'animated';
#import 'rotated-flipped';
#import 'stacked';
#import 'icons';
#import 'screen-reader';
Error: Failed to find 'variables'

Well, maybe this could help you, in the webpack.config.js you should add, specifically in purgeCss -> content, the diferents paths.
content: [
"./node_modules/name_package/**/**/*.{css,scss}"
]
I had the same issue using NextJS in production.

After a day of wasting time, finally, i found the souloution. There is a postcss plugin to do it for us!
First, install it using npm as dev dependency:
npm i postcss-url -D
Now you must update your webpack.config.js to use postcss-url. Just add postcss-url directly after postcss-import

It worked for me, when I removed the
require("postcss-import") from the webpack.config.js file. It took me 1 day to resolve it. Found the explanation here: Webpack style-loader / css-loader: url() path resolution not working

Related

How to configure minimum development environment for Tailwindcss v3 to utilize #import

The goal is to configure a development environment for Tailwindcss v3 that supports #import and the removal of unused custom CSS classes.
I am not using a bundler. The project depends on just HTML, CSS, and JS i.e. no frameworks. If it's important, I use VS Code.
This is what I've tried.
Project's configuration:
// tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
content: [
'./src/**/*.{html,js}',
],
darkMode: 'media',
theme: {
extend: {
fontFamily: {
primary: '"Inter", sans-serif',
mono: ['"DM Mono"', ...defaultTheme.fontFamily.mono]
}
},
},
variants: {
extend: {}
},
plugins: [
// ...
]
};
// postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
require('cssnano')
]
/* styles.css*/
#import "tailwindcss/base";
#import "./custom-base-styles.css";
#import "tailwindcss/components";
#import "./custom-components.css";
#import "tailwindcss/utilities";
#import "./custom-utilities.css";
And this is my commend line:
$ npx tailwindcss -i ./src/css/styles.css -o ./css/styles.css --watch
Running just the above command, the setup does not inline the custom*.css files.
To achieve inlining, I also have to execute below, which on the surface, seems as if it should not be necessary.
$ npx postcss-cli './src/css/styles.css' -o './css/styles.css' -w
The end result is, Tailwindcss is not removing my unused custom styles, even when wrapped in the required #layer {}.
Step 5 here (Using PostCSS) just says to start your build process and does not offer any details.
Step 4 here (Tailwind CLI) only says to start the Tailwind CLI process.
So, what am I missing?
Do I need to use a bundler? If so, my preferred one is Rollup.
Other details:
autoprefixer:^10.4.0
cssnano:^5.0.10
postcss-cli:^9.0.2
postcss-import:^14.0.2
tailwindcss: "^3.0.7
Your intuition is correct. You do not need to run post-cli. PostCSS will execute once you update your Tailwind command.
You are missing the --postcss parameter.
Assuming that postcss.config.js is in your project's root, copy this to the script section of your package.json:
"tw": "tailwindcss -i ./src/css/styles.css -o ./css/styles.css --postcss postcss.config.js --watch"
Then from the command line at the project's root, run:
npm run tw
Note: with the above command, Tailwind will not rebuild the output file after the HTML file has been saved. You'll need to edit and save one of the CSS source files to initiate a rebuild. (Perhaps I still have a configuration problem?)
One other item, how are you testing for the removal of unused custom classes?
The mistake I made was just commenting out the HTML utilizing the custom class and then looking at the CSS output to see if the class was removed. But Tailwind (as documented somewhere) won't remove a class if the class appears anywhere in the markup, even in a commented line. If you're testing your build process, rename the class in the markup to anything, and then Tailwind will drop the custom class from the CSS output.

Add hash to images in css webpack

Is there a way to add hash values to images that are referenced in CSS when you compile using Webpack?
I'm using React, and have separate scss files for each component file (e.g header.js & header.scss). Within some of the scss files, I have a background image. However, my server has super high caching levels, and is caching the images within the compiled css files.
What I'd like to do is, during the css compilation, add a hash value to each image reference, which would update on every build. So for example, it would compile to this:
.background-class {
background-image: url('images/my-image.jpg?0adg83af0');
}
I've tried to use the url-loader, but because these images aren't being referenced in the JS files, I don't think they're being picked up?
I ended up using a combination of PostCSS and PostCSS CacheBuster. If anyone wants to add this to their webpack setup, you need to run npm i postcss-loader postcss-cachebuster, then in your webpack.config.js, add const PostCssCacheBuster = require('postcss-cachebuster'); to the top of your file, and add the following loader config in between css-loader and sass-loader (obviously if you use this setup):
loader: 'postcss-loader',
options: {
sourceMap: true,
plugins: () => [
PostCssCacheBuster({
imagesPath: "/src/Frontend",
cssPath: "/" + distributionPath,
supportedProps: [
'background',
'background-image'
],
paramName: 'v='
})
]
}
},

How to manage multiple stylessheets and make the app efficient

I'm using Angular+2 with bootstrap 4.3.X and I want to re-style/re-theme the bootstrap scss.
I want to achieve this the best way with Angular.
I'm aware this may be done with scss only.
[scss only]
angular.json
"styles": [
"src/styles.scss"
],
styles.scss
$theme-colors: ( "primary": #f700ff );
#import '../node_modules/bootstrap/scss/bootstrap.scss';
...
But what i'm trying to achieve is below.
[Angular way]
angular.json
"styles": [
"src/styles.scss",
"node_modules/bootstrap/scss/bootstrap.scss"
],
styles.scss
$theme-colors: ( "primary": #f700ff );
...
But it does not seem to matter which order i enter in angular.json. The $theme-colors simply won't work.
The two options work very differently. [scss only] is slow, the whole bootstrap module needs to be re-compiled if any component is changed. While in [Angular way] it never re-compiles during debug, only during build process.
Also
[scss only] ng build --prod -> dist= 2,99MB
[Angular way] ng build --prod -> dist = 2,11MB
Please help providing what is missing to do it the [Angular way].
Thank you in advance!
You need to make a variables.scss file in which you declare theme-colors, add that to your
"stylePreprocessorOptions": {
"includePaths": [""]
},
in angular.json configurations
Then in styles.scss at the top declare #import 'variables.scss' then you can use the $theme-colors var in your stylesheets. in every stylesheet you need the variables you need to declare the import statement.

webpack2: how to import Bootstrap CSS for react-bootstrap to find its styles?

I am using webpack 2 and react-bootstrap in my project ; I can't find how to have bootstrap CSS styles properly applied it seems like the .css file is not loaded, no matter which import statement I tried to use.
As far as I understand I do not need the full bootstrap package with javascript etc. since I am using react-bootstrap ; I just need the CSS. So I added this in my main.js file:
import 'bootstrap/dist/css/bootstrap.css';
It seems to work (no error message) but the styles are not applied...
I configured the css loader in my webpack config file as described on webpack 2 documentation.
Any help would be appreciated :)
When setting modules: true in the css-loader, the CSS is locally scoped by default, but you need them to be available globally. The simplest solution is to remove modules: true entirely. You could still use modules in your own CSS files by using :local.
But if you would like to use modules, there are some workarounds to import globals.
Defining separate rules
Instead of enabling modules for all the CSS files, you can make two different rules, that match the desired files. So let's say all CSS imports from node_modules should be treated as regular (global) CSS. The rules would look like this:
{
// For all .css files except from node_modules
test: /\.css$/,
exclude: /node_modules/,
use: [
'style-loader',
{ loader: 'css-loader', options: { modules: true } }
]
},
{
// For all .css files in node_modules
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader']
}
Of course you can be more specific in what you want to include/exclude, if you don't want the entire node_modules.
Specifying loaders inline
You can specify the loaders in the import and webpack will use those over the configured ones. You would import bootstrap as follows:
import '!style-loader!css-loader!bootstrap/dist/css/bootstrap.css';
This is just a quick workaround without having to change any config, but it's probably not desirable, especially when having multiple such cases.

How to use font-awesome icons from node-modules

I have installed font-awesome 4.0.3 icons using npm install.
If I need to use it from node-modules how should I use it in html file?
If I need to edit the less file do I need to edit it in node-modules?
Install as npm install font-awesome --save-dev
In your development less file, you can either import the whole font awesome less using #import "node_modules/font-awesome/less/font-awesome.less", or look in that file and import just the components that you need. I think this is the minimum for basic icons:
/* adjust path as needed */
#fa_path: "../node_modules/font-awesome/less";
#import "#{fa_path}/variables.less";
#import "#{fa_path}/mixins.less";
#import "#{fa_path}/path.less";
#import "#{fa_path}/core.less";
#import "#{fa_path}/icons.less";
As a note, you still aren't going to save that many bytes by doing this. Either way, you're going to end up including between 2-3k lines of unminified CSS.
You'll also need to serve the fonts themselves from a folder called/fonts/ in your public directory. You could just copy them there with a simple gulp task, for example:
gulp.task('fonts', function() {
return gulp.src('node_modules/font-awesome/fonts/*')
.pipe(gulp.dest('public/fonts'))
})
You have to set the proper font path. e.g.
my-style.scss
$fa-font-path:"../node_modules/font-awesome/fonts";
#import "../node_modules/font-awesome/scss/font-awesome";
.icon-user {
#extend .fa;
#extend .fa-user;
}
Add the below to your .css stylesheet.
/* You can add global styles to this file, and also import other style files */
#import url('../node_modules/font-awesome/css/font-awesome.min.css');
You will need to copy the files as part of your build process. For example, you can use a npm postinstall script to copy the files to the correct directory:
"postinstall": "cp -R node_modules/font-awesome/fonts ./public/"
For some build tools, there are preexisting font-awesome packages. For example, webpack has font-awesome-webpack which lets you simple require('font-awesome-webpack').
Using webpack and scss:
Install font-awesome using npm (using the setup instructions on https://fontawesome.com/how-to-use)
npm install #fortawesome/fontawesome-free
Next, using the copy-webpack-plugin, copy the webfonts folder from node_modules to your dist folder during your webpack build process. (https://github.com/webpack-contrib/copy-webpack-plugin)
npm install copy-webpack-plugin
In webpack.config.js, configure copy-webpack-plugin. NOTE: The default webpack 4 dist folder is "dist", so we are copying the webfonts folder from node_modules to the dist folder.
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyWebpackPlugin([
{ from: './node_modules/#fortawesome/fontawesome-free/webfonts', to: './webfonts'}
])
]
}
Lastly, in your main.scss file, tell fontawesome where the webfonts folder has been copied to and import the SCSS files you want from node_modules.
$fa-font-path: "/webfonts"; // destination folder in dist
//Adapt the path to be relative to your main.scss file
#import "../node_modules/#fortawesome/fontawesome-free/scss/fontawesome";
//Include at least one of the below, depending on what icons you want.
//Adapt the path to be relative to your main.scss file
#import "../node_modules/#fortawesome/fontawesome-free/scss/brands";
#import "../node_modules/#fortawesome/fontawesome-free/scss/regular";
#import "../node_modules/#fortawesome/fontawesome-free/scss/solid";
#import "../node_modules/#fortawesome/fontawesome-free/scss/v4-shims"; // if you also want to use `fa v4` like: `fa fa-address-book-o`
and apply the following font-family to a desired region(s) in your html document where you want to use the fontawesome icons.
Example:
body {
font-family: 'Font Awesome 5 Free'; // if you use fa v5 (regular/solid)
// font-family: 'Font Awesome 5 Brands'; // if you use fa v5 (brands)
}
With expressjs, public it:
app.use('/stylesheets/fontawesome', express.static(__dirname + '/node_modules/#fortawesome/fontawesome-free/'));
And you can see it at: yourdomain.com/stylesheets/fontawesome/css/all.min.css
You could add it between your <head></head> tag like so:
<head>
<link href="./node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css">
</head>
Or whatever your path to your node_modules is.
Edit (2017-06-26) - Disclaimer: THERE ARE BETTER ANSWERS. PLEASE DO NOT USE THIS METHOD. At the time of this original answer, good tools weren't as prevalent. With current build tools such as webpack or browserify, it probably doesn't make sense to use this answer. I can delete it, but I think it's important to highlight the various options one has and the possible dos and do nots.
Since I'm currently learning node js, I also encountered this problem. All I did was, first of all, install the font-awesome using npm
npm install font-awesome --save-dev
after that, I set a static folder for the css and fonts:
app.use('/fa', express.static(__dirname + '/node_modules/font-awesome/css'));
app.use('/fonts', express.static(__dirname + '/node_modules/font-awesome/fonts'));
and in html:
<link href="/fa/font-awesome.css" rel="stylesheet" type="text/css">
and it works fine!
I came upon this question having a similar problem and thought I would share another solution:
If you are creating a Javascript application, font awesome icons can also be referenced directly through Javascript:
First, do the steps in this guide:
npm install #fortawesome/fontawesome-svg-core
Then inside your javascript:
import { library, icon } from '#fortawesome/fontawesome-svg-core'
import { faStroopwafel } from '#fortawesome/free-solid-svg-icons'
library.add(faStroopwafel)
const fontIcon= icon({ prefix: 'fas', iconName: 'stroopwafel' })
After the above steps, you can insert the icon inside an HTML node with:
htmlNode.appendChild(fontIcon.node[0]);
You can also access the HTML string representing the icon with:
fontIcon.html
If you're using npm you could use Gulp.js a build tool to build your Font Awesome packages from SCSS or LESS. This example will compile the code from SCSS.
Install Gulp.js v4 locally and CLI V2 globally.
Install a plugin called gulp-sass using npm.
Create a main.scss file in your public folder and use this code:
$fa-font-path: "../webfonts";
#import "fontawesome/fontawesome";
#import "fontawesome/brands";
#import "fontawesome/regular";
#import "fontawesome/solid";
#import "fontawesome/v4-shims";
Create a gulpfile.js in your app directory and copy this.
const { src, dest, series, parallel } = require('gulp');
const sass = require('gulp-sass');
const fs = require('fs');
function copyFontAwesomeSCSS() {
return src('node_modules/#fortawesome/fontawesome-free/scss/*.scss')
.pipe(dest('public/scss/fontawesome'));
}
function copyFontAwesomeFonts() {
return src('node_modules/#fortawesome/fontawesome-free/webfonts/*')
.pipe(dest('public/dist/webfonts'));
}
function compileSCSS() {
return src('./public/scss/theme.scss')
.pipe(sass()).pipe(dest('public/css'));
}
// Series completes tasks sequentially and parallel completes them asynchronously
exports.build = parallel(
copyFontAwesomeFonts,
series(copyFontAwesomeSCSS, compileSCSS)
);
Run 'gulp build' in your command line and watch the magic.
SASS modules version
Soon, using #import in sass will be depreciated. SASS modules configuration works using #use instead.
#use "../node_modules/font-awesome/scss/font-awesome" with (
$fa-font-path: "../icons"
);
.icon-user {
#extend .fa;
#extend .fa-user;
}

Resources