I'm using Next.JS with a few other modules. One of them, Megadraft, comes with its own CSS. I don't know if this is relevant, but I also use PurgeCSS.
Everything works fine on development mode, but the CSS seems to break in production mode. To be a little more explicit, all of the classes of Megadraft, seem to have no definition in production mode.
The HTML nodes in the inspector still show that the classes are here, but they have just no definition.
Here's how I import the said CSS files in my pages/_app.js file:
// pages/_app.js
import "css/tailwind.css";
import "megadraft/dist/css/megadraft.css";
And this is my postcss.config.js:
// postcss.config.js
const purgecss = [
"#fullhuman/postcss-purgecss",
{
content: [
"./components/**/*.js",
"./Layout/**/*.js",
"./pages/**/*.js",
"./node_modules/next/dist/pages/**/*.js",
"./node_modules/next/dist/Layout/**/*.js",
"./node_modules/next/dist/components/**/*.js"
],
defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || [],
},
];
module.exports = {
plugins: [
"postcss-import",
"tailwindcss",
"autoprefixer",
...(process.env.NODE_ENV === "production" ? [purgecss] : []),
],
};
I'm using next ^9.4.4. It may be worth noticing that TailwindCSS seems to work just fine (both in dev and prod), but I think it may be because it is used as a plugin in postcss...
Just in case also, I integrated webpack to my project to solve an error I had where the code was telling that I needed a loader:
// next.config.js
module.exports = {
cssModules: true,
webpack: (config, options) => {
config.node = {
fs: "empty",
};
config.module.rules.push({
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
use: [
options.defaultLoaders.babel,
{
loader: "url-loader?limit=100000",
},
{
loader: "file-loader",
},
],
});
return config;
},
};
Anyway, if someone has an idea of why this works in development mode and not in production, it could be of great help.
Option 1: use Tailwind CSS built-in PurgeCSS
// tailwind.config.css
module.exports = {
purge: ["./components/**/*.js", "./pages/**/*.js"],
theme: {
extend: {}
},
variants: {},
plugins: []
};
// postcss.config.js
module.exports = {
plugins: ["tailwindcss", "postcss-preset-env"]
};
Be sure to add postcss-preset-env to the package's dev dependencies with npm i --save-dev postcss-preset-env or yarn add -D postcss-preset-env.
Option 2: Manually setup purge and add "./node_modules/megadraft/dist/**/*.css" to purgecss whitelisting content array:
// tailwind.config.css
module.exports = {
theme: {
extend: {}
},
variants: {},
plugins: []
};
// postcss.config.js
const purgecss = ['#fullhuman/postcss-purgecss',{
content: ["./node_modules/megadraft/dist/**/*.css", "./components/**/*.js", "./pages/**/*.js"],
defaultExtractor: content => {
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
const innerMatches = content.match(/[^<>"'`\s.()]*[^<>"'`\s.():]/g) || []
return broadMatches.concat(innerMatches)
}
}]
module.exports = {
plugins: [
'tailwindcss',
'autoprefixer',
...process.env.NODE_ENV === 'production'
? [purgecss]
: []
]
}
There may be better solutions but these two are what I can think of.
Related
I am using nextjs with tailwindcss and i am facing the difficulty in adding postcss-nesting to my nextjs app.
Here is the configuration below for the same :
next.config.js
const withPlugins = require("next-compose-plugins");
module.exports = withPlugins([], {});
postcss.config.js
module.exports = {
plugins: [
"postcss-import",
"tailwindcss",
"autoprefixer",
"tailwindcss/nesting",
"postcss-nested",
],
};
tailwind.config.js
module.exports = {
purge: {
enabled: true,
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./src/components/**/*.{js,ts,jsx,tsx}",
],
},
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
};
In my custom css file i am trying to use it like
.toolbar_navigation_items {
li {
#apply text-3xl;
}
}
then i am getting the error
"(2:3) Nested CSS was detected, but CSS nesting has not been configured correctly.
Please enable a CSS nesting plugin *before* Tailwind in your configuration.
NOTE : I also tried changing my postcss.config.js to
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss/nesting'),
require('tailwindcss'),
require('autoprefixer'),
]
}
as mentioned in the docs but it says
A PostCSS Plugin was passed as a function using require(), but it must be provided as a string.
I had same issue
install postcss-nesting: npm install -D postcss-nesting
postcss.config.js:
module.exports = {
plugins: {
"tailwindcss/nesting": "postcss-nesting",
tailwindcss: {},
autoprefixer: {},
},
};
https://tailwindcss.com/docs/using-with-preprocessors#nesting
Had same error. When used:
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss/nesting'),
require('tailwindcss'),
require('autoprefixer'),
]
}
Got this link: https://nextjs.org/docs/messages/postcss-shape
It shows how new config should be written (remove the require('package') function wrapping the strings). New postcss.config.js:
module.exports = {
plugins: [
'postcss-import',
'tailwindcss/nesting',
'tailwindcss',
'autoprefixer',
]
}
This fixed the nesting config issue for me.
Quote:
npm i sharp
# or
yarn add sharp
https://nextjs.org/docs/messages/install-sharp
I Generate a new project with nx using nextjs preset. Then with the help of the nx dev blog post i setup tailwind css like following
postcss.config.js
const { join } = require('path');
module.exports = {
plugins: {
tailwindcss: { config: './apps/storefront/tailwind.config.js' },
// tailwindcss: {
// config: join(__dirname, 'tailwind.config.js'),
// },
autoprefixer: {},
},
}
tailwind.config.js
const { createGlobPatternsForDependencies } = require('#nrwl/react/tailwind');
module.exports = {
purge: createGlobPatternsForDependencies(__dirname),
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
next.config.js
// eslint-disable-next-line #typescript-eslint/no-var-requires
const withNx = require('#nrwl/next/plugins/with-nx');
/**
* #type {import('#nrwl/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {
// Set this to true if you would like to to use SVGR
// See: https://github.com/gregberge/svgr
svgr: false,
},
};
module.exports = withNx(nextConfig);
But now if I run the apps, tailwind CSS does not seem to be laded in the app. So what am I doing wrong, and how can fix the issue?
In tailwind.config.js, you should change "purge" into "content" which recommends by the new release version of tailwindcss and also the path should config by starting from the root path. For example: content: ['./apps/blogs/pages/**/*.{js,ts,jsx,tsx}']
YR007 answer should be marked as the correct one, both postcss.config.js and tailwind.config.js should have root references if they are inside a monorepo app, for example:
postcss.config.js
module.exports = {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: { config: './apps/backoffice/tailwind.config.js' },
autoprefixer: {},
},
};
tailwind.config.js
module.exports = {
content: [
'./apps/backoffice/index.html',
'./apps/backoffice/src/**/*.{vue,js,ts,jsx,tsx}',
],
darkMode: 'class',
theme: {
extend: {},
},
plugins: [],
};
**In Nx + Nextjs with tailwind some libs components are missing tailwindcss classes you can configure them like that in tailwind.config.js in Nextjs project **
you can point directly to libary
module.exports = {
content: [
join(__dirname, '**/*.{js,ts,jsx,tsx}'),
join('libs/ui/src/lib/**/*.{js,ts,jsx,tsx}'),
...createGlobPatternsForDependencies(__dirname),
],
}
There is a simple way for it's setup. Make a tailwind.config.js file at root and add this code
module.exports = {
mode: "jit",
purge: [
"./pages/**/*.{js,ts,jsx,tsx}",
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Create a postcss.config.js file and make this
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Then in your globals.css , simply import tailwind
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
and include this globals.css file in your app.js
Before the changes, everything was going to build and everything worked.
my next.config.js //
// const withCss = require('#zeit/next-css');
// const withSass = require('#zeit/next-sass');
class TailwindExtractor {
static extract(content) {
return content.match(/[A-Za-z0-9-_:\/]+/g) || [];
}
}
const nextConfig = {
...
};
module.exports = withPlugins([
// [ withSass, {} ],
// [ withCss, {} ],
[ withPurgeCss, {
...
}
], nextConfig);
Further, as recommended, I decided to abandon the use of the deprecated # zeit / next-css and # zeit / next-sass
I added the tailwind.config.js file as written on their website:
module.exports = {
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
And made changes to post.config.js and also installed the missing packages:
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
'postcss-preset-env': { stage: 2 },
},
};
In _app.js, I include the main.scss file:
#import "tailwindcss/preflight";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
.self-class {
#apply .bg-olive .shadow-olive .text-base .text-white .font-bold;
}
I get an error: Error: Syntax error: /home/roma/project/main.scss #applycannot be used with.bg-olivebecause.bg-oliveeither cannot be found, or its actual definition includes a pseudo-selector like :hover, :active, etc. If you're sure that.bg-oliveexists, make sure that any#importstatements are being properly processed *before* Tailwind CSS sees your CSS, as#apply can only be used for classes in the same CSS tree.
Do I have my tailwind.config.js file configured incorrectly or is the file built incorrectly in general? So where I am trying to refer to tailwind styles is not known about them yet?
Thanks for any help!
Tailwind version: v9.3.5
PostCSS Config:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
...(process.env.NODE_ENV === 'production'
? {
'#fullhuman/postcss-purgecss': {
content: ['./components/**/*.js', './pages/**/*.js'],
defaultExtractor: content =>
content.match(/[\w-/:]+(?<!:)/g) || [],
},
}
: {}),
},
}
Tailwind Config:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
tint: 'rgba(0,0,0,0.3)',
},
},
},
variants: {},
plugins: [],
}
Styles work perfectly in development but in production only some styling is working. Upon checking the CSS file in build folder, looks like some of the CSS classes are not being extracted or possibly purged therefore resulting in partial styling of the app.
EDIT : PurgeCSS version 3.0 add safelist option, replace whitelist
I faced the same problem when i dynamically inject name of some classes into my html template.
I am using nuxt.js/tailwindcss so you need to read the documentary first to solve your problem.
Problem
here's the code who generate the classes missing in production
computed: {
axeY() {
return this.y < 0 ? `-translate-y${this.y}` + ' ' : `translate-y-1` + ' '
},
axeX() {
return this.x < 0 ? `-translate-x${this.x}` : `translate-x-${this.x}`
},
PostCSS will analyse all files into the content table (declaring in the config file), noted that my files doesn't include classes with translate prefix
as you can see, my missing classes are: [translate-x-1,-translate-x-1, translate-y-1, -translate-y-1] ... the number 1 is a variable.
Solution
I need to tell purgecss to not delete those classes by adding them into a whitelist
Or you can use them into your files, for example by creating an unless file (a file analyzed by PostCSS)
You can specify content that should be analyzed by PurgeCSS with an array of filenames
Maintain your config file of TailWindCSS by specifying all cores plugins that you're using
In a complicated case you can use a regular expression in the config file.
in my case, i can directly config purge in the config file of TailWindCSS, by passing the whitelist in the options variable, and here is my config file when i use the first solution :
/*
** TailwindCSS Configuration File
**
** Docs: https://tailwindcss.com/docs/configuration
** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
*/
const num = [1, 2, 3, 4, 5, 6, 8, 10, 12]
const whitelist = []
num.map((x) => {
whitelist.push('translate-x-' + x)
whitelist.push('-translate-x-' + x)
whitelist.push('translate-y-' + x)
whitelist.push('-translate-y-' + x)
})
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
},
theme: {},
variants: {
backgroundColor: ['hover', 'focus', 'active'],
},
plugins: [],
purge: {
// Learn more on https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css
enabled: process.env.NODE_ENV === 'production',
content: [
'components/**/*.vue',
'layouts/**/*.vue',
'pages/**/*.vue',
'plugins/**/*.js',
'nuxt.config.js',
],
options: {
whitelist,
},
},
}
Found the issue, postcss config was missing sections folder in content array and also since my js files had jsx, i need to add that as well.
// 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: ['./components/**/*.jsx', './pages/**/*.js', './sections/**/**/*.jsx'],
defaultExtractor: content =>
content.match(/[\w-/:]+(?<!:)/g) || [],
},
}
: {}),
},
}
I struggled with this same issue. Tailwind will purge classes created with string concatenation.
One solve is to save class names as variables.
n = ‘row-start-2’;
className={n}
For my use case I couldn't do this. Instead, I used the safelist greedy option.
In tailwind config file:
module.exports = {
purge: {
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
options: {
safelist: {
greedy: ["/safe$/"],
},
},
},
Then I added the "safe" class to all elements where I'm using string concatenation to create classes.
className={`safe sm:grid-cols-${smCols} sm:grid-rows-${smRows} md:grid-cols-${mdCols} md:grid-rows-${mdRows}
Struggling trying to get Angular Material , or any 3rd party control set really, to work with this new template. In this new template, the webpack is broken into TreeShakable and NonTreeShakable. In addition the app.module is now app.module.shared, app.module.browser, app.module.server.
As I have attempted to get this to work, the app will only run with modules configured in app.module.browser, but the material tags are not getting processed. Trying something simple and trying the button. I don't get any errors but not does it work. I went to their example in Plunker, copied what it generated, and it displays right telling me I got the css in right, at least.
Including the webpack vendor configuration as a starting point, as this seems to be key because how it bundles the css and js.
TIA
Webpack.vendor
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const merge = require('webpack-merge');
const treeShakableModules = [
'#angular/animations',
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/forms',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/router',
'#angular/material',
'#angular/cdk',
'zone.js'
];
const nonTreeShakableModules = [
'bootstrap',
'jqwidgets-framework',
"#angular/material/prebuilt-themes/indigo-pink.css",
'bootstrap/dist/css/bootstrap.css',
'font-awesome/css/font-awesome.css',
'es6-promise',
'es6-shim',
'event-source-polyfill',
'jquery',
];
const allModules = treeShakableModules.concat(nonTreeShakableModules);
module.exports = (env) => {
const extractCSS = new ExtractTextPlugin('vendor.css');
const isDevBuild = !(env && env.prod);
const sharedConfig = {
stats: { modules: false },
resolve: { extensions: [ '.js' ] },
module: {
rules: [
{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
]
},
output: {
publicPath: 'dist/',
filename: '[name].js',
library: '[name]_[hash]'
},
plugins: [
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.ContextReplacementPlugin(/\#angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)#angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898
new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
]
};
const clientBundleConfig = merge(sharedConfig, {
entry: {
// To keep development builds fast, include all vendor dependencies in the vendor bundle.
// But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle.
vendor: isDevBuild ? allModules : nonTreeShakableModules
},
output: { path: path.join(__dirname, 'wwwroot', 'dist') },
module: {
rules: [
{ test: /\.css(\?|$)/, use: extractCSS.extract({ use: isDevBuild ? 'css-loader' : 'css-loader?minimize' }) }
]
},
plugins: [
extractCSS,
new webpack.DllPlugin({
path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
name: '[name]_[hash]'
})
].concat(isDevBuild ? [] : [
new webpack.optimize.UglifyJsPlugin()
])
});
const serverBundleConfig = merge(sharedConfig, {
target: 'node',
resolve: { mainFields: ['main'] },
entry: { vendor: allModules.concat(['aspnet-prerendering']) },
output: {
path: path.join(__dirname, 'ClientApp', 'dist'),
libraryTarget: 'commonjs2',
},
module: {
rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] } ]
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
name: '[name]_[hash]'
})
]
});
return [clientBundleConfig, serverBundleConfig];
}
You need to include angular material and cdk in nonTreeShakableModules like:
const treeShakableModules = [
'#angular/animations',
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/forms',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/router',
'zone.js',
];
const nonTreeShakableModules = [
'bootstrap',
'bootstrap/dist/css/bootstrap.css',
'#angular/material',
'#angular/material/prebuilt-themes/indigo-pink.css',
'#angular/cdk',
'es6-promise',
'es6-shim',
'event-source-polyfill',
'jquery',
];
Make sure you have installed both angular material and cdk modules from npm with the following 2 commands (animations module is optional):
npm install --save #angular/material #angular/cdk
npm install --save #angular/animations
This should add the following lines in package.json:
"#angular/animations": "https://registry.npmjs.org/#angular/animations/-/animations-4.2.5.tgz",
"#angular/cdk": "^2.0.0-beta.8",
"#angular/material": "^2.0.0-beta.8",
You now should try executing webpack build with following command in cmd or PowerShell:
webpack --config webpack.config.vendor.js
If there are no errors you can include the components you want to use in app.module.shared.ts:
// angular material modules
import {
MdAutocompleteModule,
MdButtonModule,
MdButtonToggleModule,
MdCardModule,
MdCheckboxModule,
MdChipsModule,
MdCoreModule,
MdDatepickerModule,
MdDialogModule,
MdExpansionModule,
MdGridListModule,
MdIconModule,
MdInputModule,
MdListModule,
MdMenuModule,
MdNativeDateModule,
MdPaginatorModule,
MdProgressBarModule,
MdProgressSpinnerModule,
MdRadioModule,
MdRippleModule,
MdSelectModule,
MdSidenavModule,
MdSliderModule,
MdSlideToggleModule,
MdSnackBarModule,
MdSortModule,
MdTableModule,
MdTabsModule,
MdToolbarModule,
MdTooltipModule,
} from '#angular/material';
import { CdkTableModule } from '#angular/cdk';
and add them to imports in #NgModule
There are still some components that are bugged until next fixes. Like the checkbox component which breaks server-side rendering when you refresh page. But it will be fixed in the next release (it has been already on master branch).
Using latest Angular Material in ASP.net Core 2.0 with default installed node packages is more difficult and time consuming for resolving package dependencies.
Use below version of angular material in package.json
"#angular/cdk": "^2.0.0-beta.12"
"#angular/material": "^2.0.0-beta.12"
followed by run below command to install it.
npm install --save