How can I purge CSS based on the HTML output? - javascript

I'm currently working on a project in Next.js where we use custom SCSS combined with Bootstrap SCSS and React-Bootstrap. We noticed our main.scss file (where our custom SCSS and Bootstrap SCSS is bundled) is getting too heavy.
When I checked in the coverage tab in Chrome, I noticed 95% of the CSS is not being used. So I want to get rid of all the CSS which is in the Bootstrap SCSS files and which is not being used.
I found that you can purge CSS by adding a postcss.config.js file in Next.js and then adding purgeCSS there (https://purgecss.com/guides/next.html).
The problem is that this is based on the source code (JS,JSX,TS,TSX files).
In the source file we use react-bootstrap components, which use some of the CSS in the Bootstrap SCSS files too. This is not picked up by purgeCSS because the classes are not literally in the source code.
This is why I want the purgeCSS to run on the output HTML from Next.js.
For this I adapted the build task in my package.json to this:
"build": "next build && purgecss --css ./.next/static/**/*.css --content ./.next/server/**/*.html --output ./.next/static/css",
This works fine, the CSS output is exactly what I want. But I don't know how to make it so Next.js actually uses this new CSS file on the website instead of the unoptimized CSS. Any ideas?

If you use regex with the safelist attribute in your postcss.config.js file, then purgecss will work correctly. We had the same problem with bootstrap-react, purgecss, and next.js. Here's our postcss.config.js:
module.exports = {
plugins: [
"postcss-flexbugs-fixes",
[
"postcss-preset-env",
{
autoprefixer: { flexbox: "no-2009" },
stage: 3,
features: { "custom-properties": false }
}
],
[
"#fullhuman/postcss-purgecss",
{
content: ['./components/**/*.{js,jsx,ts,tsx}', "./pages/**/*.{js,jsx,ts,tsx}"],
css: ['./styles/**/*.{css,scss}'],
defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
safelist: {
standard: ["html", "body", "btn" ],
deep: [/^col/, /^navbar/, /^nav/]
}
}
]
]};
Basically, I've added the regex expressions to safelist any classes that start with "col", "navbar", and "nav". Just inspect any of the classes that aren't getting picked up and add the corresponding regex expression.

Related

Bootstrap 5 with Grunt to bundle and optimise js

I have successfully used Grunt to bundle the bootstrap 5 scss together into a single file. I have it setup so I can add and remove the component scss to the needs of the project for optimisation.
I am now trying to do the same with the js.
I am using grunt-contrib-uglify with the following task:
uglify: {
site: {
options: {
sourcemap: false
},
files: {
'example/static/example/assets/js/example.min.js': [
// popper bs5 dependency
'node_modules/#popperjs/core/dist/umd/popper.js',
// bootstrap 5 core js
'node_modules/bootstrap/js/dist/dom/data.js',
'node_modules/bootstrap/js/dist/dom/event-handler.js',
'node_modules/bootstrap/js/dist/dom/manipulator.js',
'node_modules/bootstrap/js/dist/dom/selector-engine.js',
// component js
// note ordering of components can be important
// eg. popover relies on tooltip, therefore tooltip must therefore go first
'node_modules/bootstrap/js/dist/base-component.js',
'node_modules/bootstrap/js/dist/alert.js',
'node_modules/bootstrap/js/dist/button.js',
'node_modules/bootstrap/js/dist/carousel.js',
'node_modules/bootstrap/js/dist/collapse.js',
'node_modules/bootstrap/js/dist/dropdown.js',
'node_modules/bootstrap/js/dist/modal.js',
'node_modules/bootstrap/js/dist/offcanvas.js',
'node_modules/bootstrap/js/dist/scrollspy.js',
'node_modules/bootstrap/js/dist/tab.js',
'node_modules/bootstrap/js/dist/toast.js',
'node_modules/bootstrap/js/dist/tooltip.js',
'node_modules/bootstrap/js/dist/popover.js',
// custom js
'example/src/js/**/*.js'
]
}
},
},
I include it in my html, then have a script below it eg.
<script>
var myOffcanvas = document.getElementById('offcanvasExample')
var bsOffcanvas = new bootstrap.Offcanvas(myOffcanvas)
</script>
I get the error:
ReferenceError: bootstrap is not defined
in the console. What am I missing from the bundle to make this work? I have used the npm bs5 starter on Github as a reference for the files eg. popper dependency, the core js and imported all other component files in the node_modules/bootstrap/js/dist folder.
Github Bootstrap 5 npm starter
Bootstrap 5.1
Popper 2.9.2
Edit: Works correctly with distributed bundle.
so using the same grunt uglify task above, you can:
<script>
var myOffcanvas = document.getElementById('offcanvasExample')
var bsOffcanvas = Offcanvas(myOffcanvas)
</script>
(Note the removal of new bootstrap. from the script). I think this is because when importing the component files separately you are not importing Bootstrap as a module. Therefore unlike the bundle it is not available.
However, the components as separate functions are imported and available (much like Offcanvas in the above example)
EDIT
As bootstrap 5 uses rollup to bundle its javascript I have now looked into the 'grunt-rollup' task. I was not happy in the respect that my previous answer did not align with the bootstrap docs.
I have since got it working successfully with the grunt-rollup task with this configuration:
// gruntfile imports
const babel = require("#rollup/plugin-babel").default;
const path = require('path')
const {nodeResolve} = require("#rollup/plugin-node-resolve");
// rollup task
rollup: {
options: {
plugins: [
babel({
exclude: './node_modules/**',
babelHelpers: 'bundled',
}),
nodeResolve()
],
globals: {
'#popperjs/core': 'Popper'
},
external: ['#popperjs/core'],
format: 'umd',
name: 'bootstrap',
},
files: {
src: path.resolve(__dirname, `path/to/bootstrap5/imports/file`),
dest: path.resolve(__dirname, `path/to/export/file`),
},
},
where the bootstrap imports file looks like:
import Alert from 'node_modules/bootstrap/js/src/alert'
import Button from 'node_modules/bootstrap/js/src/button'
import Carousel from 'node_modules/bootstrap/js/src/carousel'
import Collapse from 'node_modules/bootstrap/js/src/collapse'
import Dropdown from 'node_modules/bootstrap/js/src/dropdown'
import Modal from 'node_modules/bootstrap/js/src/modal'
import Offcanvas from 'node_modules/bootstrap/js/src/offcanvas'
import Popover from 'node_modules/bootstrap/js/src/popover'
import ScrollSpy from 'node_modules/bootstrap/js/src/scrollspy'
import Tab from 'node_modules/bootstrap/js/src/tab'
import Toast from 'node_modules/bootstrap/js/src/toast'
import Tooltip from 'node_modules/bootstrap/js/src/tooltip'
export default {
Alert,
Button,
Carousel,
Collapse,
Dropdown,
Modal,
Offcanvas,
Popover,
ScrollSpy,
Tab,
Toast,
Tooltip
}
now you can choose which components to have in the js.
I'm not a super expert and not sure if the question is clear enough, but I would not recommend concatenating Libraries like Boostrap into a single main file in together other JS files outside the framework, due to performance issues and possible crashes in between libs due to definitions, and the possibility to update without a build process and also your site might be penalized by google engine.
Besides that, Boostrap normally already provides .min.css and .min.js already compressed/minified/uglified ready to use if you are not gonna change anything from the original design patterns, avoid using uncompress files if you are not gonna customize it.
for the rest in regards to other custom libraries or vanilla's JS create by you, you can use grunt-contrib-concat also if you wanna check the performance you can use
PageSpeed Insights with the result will know what exactly needs to be applied to get better performance and optimization.

NextJS: TailwindCSS not working in production

Iā€™m using NextJS, TailwindCSS and Typescript. When running in development, everything works as expected, but when running in production, no tailwindcss classes are applied. Here is a link to the repo: https://github.com/Capsule-app/next.
This problems occurred because of Purge. Please check the official page
Few possible way to fixed this issues.
Option A (Quick & Dirty Plus Lazy) :
In tailwind.config.js file try purge: false
Option B:
If you are using purge: ["./pages/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,ts,jsx,tsx}"], Or If you have custom css class or third-party lib class then follow Safelisting with `install #fullhuman/postcss-purgecss first then try
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
...(process.env.NODE_ENV === 'production'
? {
'#fullhuman/postcss-purgecss': {
// added sections folder and changed extension to jsx
content: ['./src/components/**/*.jsx', './src/pages/**/*.js'],
defaultExtractor: content =>
content.match(/[\w-/:]+(?<!:)/g) || [],
},
}
: {}),
},
}`
Note: Each solution depend on it's context. I request to you read official docs carefully.

How to remove comments when building TypeScript into JavaScripts using rollup

I am using rollup to build my TypeScript sources. I want to remove comments ONLY, without any minification, in order to hot update code when debugging.
I have tried rollup-plugin-terser, it can remove comments but it will also minify my code somehow, I cannot completely disable the minification.
How can I do that? Thanks!
Like #jujubes answered in the comments, the rollup-plugin-cleanup will do the task. I want to expand a bit.
Three things:
Add ts to extensions list, like extensions: ["js", "ts"] ā€” otherwise sources won't be processed, even if transpiling step typescript() is before it ā€” I originally came here investigating why rollup-plugin-cleanup won't work on TS files and it was just ts extension missing šŸ¤¦ā€ā™‚ļø
code coverage is important; on default settings, this plugin would remove istanbul statements like /* istanbul ignore else */ so it's good to exclude them, comments: "istanbul",
removing console.log is a separate challenge which is done with #rollup/plugin-strip and it goes in tandem to rollup-plugin-cleanup. In my case, depending is it a "dev" or "prod" Rollup build (controlled by a CLI flag --dev, as in rollup -c --dev), I remove console.log on prod builds only. But comments are removed on both dev and prod builds.
currently, I use:
import cleanup from "rollup-plugin-cleanup";
...
{
input: "src/main.ts",
output: ...,
external: ...,
plugins: [
...
cleanup({ comments: "istanbul", extensions: ["js", "ts"] }),
...
Here's an example of rollup-plugin-cleanup being used my Rollup config, here's my Rollup config generator (in monorepos, Rollup configs are hard to maintain by hand so I generate them). If you decide to wire up --dev CLI flag, the gotcha is you have to remove the flag from the commandLineArgs before script ends, otherwise Rollup will throw, see the original tip and it in action.
You should be able to achieve this too with just rollup-plugin-terser. It bases on terser so more information it's actually available on its README, here is the part related to minification. So in your case this part of rollup.config.js should looks like:
plugins: [
terser({
// remove all comments
format: {
comments: false
},
// prevent any compression
compress: false
}),
],
Keep in mind, that you can also enable part of configuration for production only. So having declared production const in your rollup.config.js you can do like that:
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
plugins: [
production && terser({
// terser plugin config here
}),
],
};

How to stop purging tailwind components

I am using TailwindCSS and Laravel Mix. I am trying to setup PurgeCSS and I have got it reading my template files (working with WordPress) and purge any CSS not within the template files.
However, as part of Tailwind, I am using #apply in my scss files and those utilities I am applying are also being purged which leaves me with a non functioning site.
My sass files are in css/dev and there is an app.scss and then directories with more files within them (base, utilities, custom, components).
My webpack.mix.js file configuration is as follows:
mix.scripts(['js/dev/app.js', 'js/dev/navigation.js', 'js/dev/skip-link-focus-fix.js'],
'js/build/app.js')
.sass('css/dev/app.scss', 'css/build')
.options({
processCssUrls: false,
postCss: [tailwindcss('./tailwind.config.js')],
})
.purgeCss({
enabled: mix.inProduction(),
// Your custom globs are merged with the default globs. If you need to
// fully replace the globs, use the underlying `paths` option instead.
globs: [
path.join(__dirname, 'template-parts/*.php'),
path.join(__dirname, '*.php'),
path.join(__dirname, 'css/dev/*.scss'),
path.join(__dirname, 'css/dev/**/*.scss'),
],
extensions: ['html', 'js', 'php', 'scss', 'css'],
});
As you can see, I tried setting the paths for purgeCss to look inside the css paths but that has not worked.
Does anyone know how to achieve this?
You are compiling your scss to css before Purge runs, so there should be no need to purge your .scss files only your main.css (or whatever the output is called).
Do your compiled class names actually exist in full in your template files? If they are not a 100% match for the classes in your templates then they will, quite correctly, be purged.
The issue was with WordPress classes not being included in the template files and then being purged. The solution was switching to using UnCSS which allowed me to setup URLS for UnCSS to visit and it won't purge any classes used on those pages. I also included some standard WordPress classes which I found a list of online.
My final config is:
const uncss = require('postcss-uncss');
mix.js('js/dev/app.js', 'js/build/app.js')
.sass('css/dev/app.scss', 'css/build')
.options({
processCssUrls: false,
postCss: [
tailwindcss('./tailwind.config.js'),
...process.env.NODE_ENV === 'production' ? [uncss({
html: [
'./*.php',
'./template-parts/*.php',
'https://www.example.com',
'https://www.example.com/work/',
'https://www.example.com/work/example-project/',
'https://www.example.com/contact/',
'https://www.example.com/blog/',
'https://www.example.com/blog/laravel-php-framework/',
],
ignore: [
'.rtl',
'.home',
'.blog',
'.archive',
'.date',
'.error404',
'.logged-in',
'.admin-bar',
'.no-customize-support',
'.custom-background',
'.wp-custom-logo',
'.alignnone',
'.alignright',
'.alignleft',
'.wp-caption',
'.wp-caption-text',
'.screen-reader-text',
'.comment-list',
'.grecaptcha-badge',
/^search(-.*)?$/,
/^(.*)-template(-.*)?$/,
/^(.*)?-?single(-.*)?$/,
/^postid-(.*)?$/,
/^attachmentid-(.*)?$/,
/^attachment(-.*)?$/,
/^page(-.*)?$/,
/^(post-type-)?archive(-.*)?$/,
/^author(-.*)?$/,
/^category(-.*)?$/,
/^tag(-.*)?$/,
/^tax-(.*)?$/,
/^term-(.*)?$/,
/^(.*)?-?paged(-.*)?$/,
'.animate',
'.animated',
'.bounce',
'.fadeInDown',
'.fadeIn',
'.fadeInUp',
'.jackInTheBox',
]
})] : [],
]
});
I also made use of the UnCSS exclude from purge CSS comments:
/* uncss:ignore start */
my css goes here
/* uncss:ignore end */
I ended up using this on all my custom sass files except for the tailwind files so that the only selectors that are purged are tailwind utilities, which saved me about 300 KB.

vuejs - how to add vue-material theme

I am starting a vuejs project using vue-cli.
I want to use vue-material as the main look and feel but I am not sure how to change the theme color.
from vue-material:
To use custom themes you'll need SCSS/SASS support in your project.
Read more about Pre-Processors. In the near future you'll be able to
use themes with Plain CSS and Stylus too.
and provide with this code:
#import "~vue-material/dist/theme/engine"; // Import the theme engine
#include md-register-theme("default", (
primary: md-get-palette-color(green, A200), // The primary color of your application
accent: md-get-palette-color(pink, 500) // The accent or secondary color
));
#import "~vue-material/dist/theme/all"; // Apply the theme
which I created a style.scss to include them.
and from vuejs come with this code:
module.exports = {
module: {
rules: [
// ... other rules omitted
// this will apply to both plain `.scss` files
// AND `<style lang="scss">` blocks in `.vue` files
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
]
},
// plugin omitted
}
First of all, there isn't any webpack.config.js. but there is a babel.config.js. So i created webpack.config.js and include the code too.
When I run npm run serve, nothing seems to happen. there isn't any error or warning too.
I am new with webpack and I really not sure how this all work.
vue-cli doesn't have a webpack.config.js file. You don't need to make one. Instead, you can put your options inside build/webpack.base.conf.js.

Categories

Resources