'NextJs Tailwind build (purge) removes all styling
In my current NextJS project, I am using Tailwind as a CSS framework. When I run yarn dev
, everything works fine, but whenever I run yarn build
followed by a yarn start
, all of my CSS seems to be purged as the layout of my page is completely different.
My tailwind.config.js file:
/* eslint-disable global-require */
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
screens: {
sm: '640px',
md: '768px',
max_md: { max: '767px' },
lg: '1024px',
xl: '1536px',
},
colors: {
primary: '#f2a53f',
white: '#fff',
},
fontFamily: {
title: ['Dancing Script', 'Times New Roman', 'sans-serif'],
sans: ['Roboto', ...defaultTheme.fontFamily.sans],
},
textShadow: {
default: '1px 1px 3px #000',
},
zIndex: {
1: -1,
},
extend: {
height: {
128: '32rem',
},
margin: {},
},
},
variants: {
extend: {},
},
plugins: [require('tailwindcss-textshadow')],
};
postcss.config.js file:
const purgecss = [
'@fullhuman/postcss-purgecss',
{
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
},
];
module.exports = {
plugins: [
'tailwindcss',
process.env.NODE_ENV === 'production' ? purgecss : undefined,
],
};
_app.tsx:
import Head from 'next/head';
import { ApolloProvider } from '@apollo/client';
import { AppProps } from 'next/app';
import { useApollo } from '../../apollo/client';
import '../styles/globals.css';
const MyApp = ({ Component, pageProps }: AppProps) => (
<ApolloProvider client={useApollo(pageProps.initialApolloState)}>
<Component {...pageProps} />
</ApolloProvider>
);
export default MyApp;
globals.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'Dancing Script';
font-style: medium;
font-weight: 500;
font-display: swap;
src: local('Dancing Script'),
url(/fonts/DancingScript-Medium.tff) format('tff');
}
body {
margin: 0 !important;
}
package.json dependencies:
"dependencies": {
"@apollo/client": "3.3.12",
"@apollo/react-hooks": "4.0.0",
"@contentful/rich-text-react-renderer": "14.1.2",
"@contentful/rich-text-types": "14.1.2",
"apollo-cache-inmemory": "1.6.6",
"apollo-client": "2.6.10",
"apollo-link-http": "1.5.17",
"autoprefixer": "10.2.5",
"clsx": "1.1.1",
"contentful": "8.2.0",
"graphql": "15.5.0",
"graphql-tag": "2.11.0",
"next": "10.0.9",
"next-with-apollo": "5.1.1",
"postcss": "8.2.8",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-icons": "4.2.0",
"tailwindcss": "2.0.4",
"tailwindcss-textshadow": "2.1.3"
},
"devDependencies": {
"@commitlint/cli": "12.0.1",
"@commitlint/config-conventional": "12.0.1",
"@fullhuman/postcss-purgecss": "4.0.3",
"@types/node": "14.14.35",
"@types/react": "17.0.3",
"@types/react-dom": "17.0.2",
"@typescript-eslint/eslint-plugin": "4.18.0",
"@typescript-eslint/parser": "4.18.0",
"add": "2.0.6",
"commitizen": "4.2.3",
"cz-conventional-changelog": "3.3.0",
"eslint": "7.22.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-prettier": "8.1.0",
"eslint-import-resolver-typescript": "2.4.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "5.1.3",
"lint-staged": "10.5.4",
"postcss-preset-env": "6.7.0",
"prettier": "2.2.1",
"typescript": "4.2.3",
"yarn": "1.22.10"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
If there is anything that seems off, feel free to let me know. I have looked at other issues online but did not find and solution to my problem.
Solution 1:[1]
Double check the paths in tailwind.config.js
. If your components are not in the components
directory, then you need to update the purge
paths to reflect this.
Solution 2:[2]
I think I see the issue. @fullhuman/postcss-purgecss
has been bundled with tailwindcss since v1.4 or so. Since it's manually declared in your postcss file as well, it's being executed twice which is wiping all of your styles. Also the fact that it only wipes your styles in production indicates that it is the culprit as execution of that particular package is dependent on process.env.NODE_ENV===production
. Removing it from your postcss file should resolve your issue since it is already being executed under the hood by the tailwindcss bundle.
Here is my postcss with [email protected]
for comparison:
postcss.config.js
module.exports = {
plugins: [
'postcss-import',
'tailwindcss',
'postcss-nesting',
'postcss-flexbugs-fixes',
[
'postcss-preset-env',
{
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3,
features: {
'custom-properties': false,
'nesting-rules': true
}
}
]
]
};
One other possibility is that you aren't wrapping the paths/file-types targeted for purging within a top-level content
object. From the code provided for your tailwind.config.js
file:
module.exports = {
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
//...
}
Here is my tailwind.config.js
for cross-comparison
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
important: true,
purge: {
content: [
'./components/**/*.{js,ts,jsx,tsx}',
'./pages/**/*.{js,ts,jsx,tsx}'
],
options: {
safelist: {
standard: ['outline-none']
}
}
},
darkMode: 'class',
theme: {
lineClamp: {
1: 1,
2: 2,
3: 3,
4: 4
},
extend: {
zIndex: {
'-10': '-10',
100: '100',
150: '150'
},
maxWidth: {
'9xl': '121rem', // 1936px
'8xl': '96rem' // 1536px
},
screens: {
xs: '375px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1440px',
'3xl': '1920px'
},
transitionDuration: {
0: '0ms',
300: '300ms',
500: '500ms',
700: '700ms',
1000: '1000ms'
},
rotate: {
0: '0deg',
45: '45deg',
90: '90deg',
125: '125deg',
180: '180deg',
270: '270deg',
360: '360deg'
},
fontFamily: {
header: ['goudy-bookletter-1911', 'serif'],
poppins: ['poppins', 'sans-serif'],
somaRoman: ['neue-haas-grotesk-text', 'sans-serif'],
somaDisplay: ['neue-haas-grotesk-display', 'sans-serif'],
sans: ['Inter var', ...defaultTheme.fontFamily.sans]
},
colors: {
'reddit-0': 'var(--reddit-0)',
'reddit-1': 'var(--reddit-1)',
'reddit-2': 'var(--reddit-2)',
'reddit-3': 'var(--reddit-3)',
'reddit-4': 'var(--reddit-4)',
'primary-0': 'var(--primary-0)',
'primary-1': 'var(--primary-1)',
'primary-2': 'var(--primary-2)',
'primary-3': 'var(--primary-3)',
'primary-4': 'var(--primary-4)',
'primary-5': 'var(--primary-5)',
'primary-6': 'var(--primary-6)',
'primary-7': 'var(--primary-7)',
'primary-8': 'var(--primary-8)',
'primary-9': 'var(--primary-9)',
'secondary-0': 'var(--secondary-0)',
'secondary-1': 'var(--secondary-1)',
'secondary-2': 'var(--secondary-2)',
'accents-0': 'var(--accents-0)',
'accents-1': 'var(--accents-1)',
'accents-2': 'var(--accents-2)',
'accents-3': 'var(--accents-3)',
'accents-4': 'var(--accents-4)',
'accents-5': 'var(--accents-5)',
'accents-6': 'var(--accents-6)',
'accents-7': 'var(--accents-7)',
'accents-8': 'var(--accents-8)',
'accents-9': 'var(--accents-9)',
'theme-0': 'var(--theme-0)',
'theme-1': 'var(--theme-1)',
lightBlue: {
0: '#E3F8FF',
100: '#B3ECFF',
200: '#81DEFD',
300: '#5ED0FA',
400: '#40C3F7',
500: '#2BB0ED',
600: '#1992D4',
700: '#127FBF',
800: '#0B69A3',
900: '#035388'
},
cyan: {
0: '#E0FCFF',
100: '#BEF8FD',
200: '#87EAF2',
300: '#54D1DB',
400: '#38BEC9',
500: '#2CB1BC',
600: '#14919B',
700: '#0E7C86',
800: '#0A6C74',
900: '#044E54'
},
rojo: {
0: '#610316',
100: '#8A041A',
200: '#AB091E',
300: '#CF1124',
400: '#E12D39',
500: '#EF4E4E',
600: '#F86A6A',
700: '#FF9B9B',
800: '#FFBDBD',
900: '#FFE3E3'
},
rosado: {
0: '#620042',
100: '#870557',
200: '#A30664',
300: '#BC0A6F',
400: '#DA127D',
500: '#E8368F',
600: '#F364A2',
700: '#FF8CBA',
800: '#FFB8D2',
900: '#FFE3EC'
},
amarillo: {
0: 'hsl(15, 86%, 30%)',
100: 'hsl(22, 82%, 39%)',
200: 'hsl(29, 80%, 44%)',
300: 'hsl(36, 77%, 49%)',
400: 'hsl(42, 87%, 55%)',
500: 'hsl(44, 92%, 63%)',
600: 'hsl(48, 94%, 68%)',
700: 'hsl(48, 95%, 76%)',
800: 'hsl(48, 100%, 88%)',
900: 'hsl(49, 100%, 96%)'
},
verdeAzulado: {
// blueish-green === teal (espanol)
0: 'hsl(170, 97%, 15%)',
100: 'hsl(168, 80%, 23%)',
200: 'hsl(166, 72%, 28%)',
300: 'hsl(164, 71%, 34%)',
400: 'hsl(162, 63%, 41%)',
500: 'hsl(160, 51%, 49%)',
600: 'hsl(158, 58%, 62%)',
700: 'hsl(156, 73%, 74%)',
800: 'hsl(154, 75%, 87%)',
900: 'hsl(152, 68%, 96%)'
},
redditRed: '#FF4500',
redditNav: '#1A1A1B',
redditSearch: '#272729',
redditBG: '#141415'
},
keyframes: {
wiggle: {
'0%, 100%': { transform: 'rotate(-3deg)' },
'50%': { transform: 'rotate(3deg)' }
},
hero: {
transform: 'translate3d(0px, 0px, 0px)'
}
},
animation: {
wiggle: 'wiggle 10s ease-in-out infinite',
hero: 'hero 1s ease-in-out infinite',
slowPing: 'pulse 10s cubic-bezier(0, 0, 0.2, 1) infinite'
},
width: {
82: '20.5rem',
100: '25rem',
200: '50rem',
'8xl': '96rem'
},
height: {
75: '75vh'
},
spacing: {
7: '1.75rem',
14: '3.5rem',
18: '4.5rem',
25: '6.25rem',
26: '6.5rem',
28: '7rem',
44: '11rem',
82: '20.5rem',
100: '25rem',
104: '26rem',
156: '39rem'
},
boxShadow: {
'outline-2': '0 0 0 2px var(--accents-0)',
magical:
'rgba(0, 0, 0, 0.02) 0px 30px 30px, rgba(0, 0, 0, 0.03) 0px 0px 8px, rgba(0, 0, 0, 0.05) 0px 1px 0px',
cardHover:
'0 4px 4.1px rgba(0, 0, 0, 0.012),0 4.9px 5.8px rgba(0, 0, 0, 0.018),0 6.3px 8.4px rgba(0, 0, 0, 0.029),0 8.8px 12.9px rgba(0, 0, 0, 0.05),0 15px 23px rgba(0, 0, 0, 0.11)'
}
},
variants: {
padding: [
'responsive',
'group-hover',
'hover',
'focus',
'even',
'odd',
'first',
'last'
],
textColor: [
'responsive',
'group-hover',
'hover',
'focus',
'even',
'first',
'last',
'odd'
],
backgroundColor: [
'responsive',
'group-hover',
'hover',
'focus',
'even',
'first',
'last',
'odd'
],
display: ['responsive', 'hover', 'group-hover'],
visibility: ['responsive', 'hover', 'group-hover'],
transitionDuration: ['responsive', 'hover', 'group-hover'],
gridColumn: ['responsive', 'hover', 'first', 'odd', 'even'],
extend: {
ringWidth: [
'responsive',
'hover',
'active',
'focus',
'group-hover'
],
ringColor: [
'responsive',
'hover',
'active',
'focus',
'group-hover'
],
fontSize: ['responsive', 'last', 'first', 'hover', 'focus'],
stroke: ['responsive', 'hover', 'focus', 'group-hover'],
fill: ['responsive', 'hover', 'focus', 'group-hover'],
gridTemplateColumns: [
'responsive',
'last',
'first',
'hover',
'focus'
],
animation: [
'responsive',
'hover',
'focus',
'motion-safe',
'motion-reduce'
],
transitionProperty: [
'responsive',
'hover',
'focus',
'motion-safe',
'motion-reduce'
],
transitionDuration: ['responsive', 'hover', 'focus'],
transitionTimingFunction: ['responsive', 'hover', 'focus'],
transitionDelay: ['responsive', 'hover', 'focus'],
scale: [
'responsive',
'hover',
'focus',
'active',
'group-hover'
],
rotate: [
'responsive',
'hover',
'focus',
'active',
'group-hover'
]
}
}
},
plugins: [
require('tailwindcss-line-clamp'),
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio')
]
};
If you're wondering why I use the postcss-import
package before tailwindcss
in my postcss.config.js, it's for separation of concerns and cleaner code. I have a top-level styles
directory containing index.css
, base.css
, utilities.css
, components.css
, and chrome-bug.css
. Their contents are as follows:
index.css
@import 'tailwindcss/base';
@import './base.css';
@import 'tailwindcss/components';
@import './components.css';
@import 'tailwindcss/utilities';
@import './utilities.css';
components.css
.fit {
min-height: calc(100vh - 88px);
}
utilities.css
#tsparticles {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
margin: 0;
}
.skeleton {
display: block;
width: 100%;
border-radius: 5px;
background-image: linear-gradient(
270deg,
var(--accents-1),
var(--accents-2),
var(--accents-2),
var(--accents-1)
);
background-size: 400% 100%;
animation: loading 8s ease-in-out infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
base.css
#__next {
display: flex;
flex-direction: column;
min-height: 100vh;
}
:root {
--reddit-0: hsl(240, 2%, 8%);
--reddit-1: hsl(240, 2%, 10%);
--reddit-2: hsl(240, 3%, 16%);
--reddit-3: hsl(16, 100%, 50%);
--primary-0: hsl(209, 61%, 16%);
--primary-1: hsl(211, 39%, 23%);
--primary-2: hsl(209, 34%, 30%);
--primary-3: hsl(209, 28%, 39%);
--primary-4: hsl(210, 22%, 49%);
--primary-5: hsl(209, 23%, 60%);
--primary-6: hsl(211, 27%, 70%);
--primary-7: hsl(210, 31%, 80%);
--primary-8: hsl(212, 33%, 89%);
--primary-9: hsl(210, 36%, 96%);
--secondary-0: #d7be69;
--secondary-1: #486581;
--secondary-2: #9fb3c8;
--accents-0: hsl(195, 7%, 11%);
--accents-1: hsl(140, 2%, 26%);
--accents-2: hsl(0, 0%, 49%);
--accents-3: hsl(0, 0%, 64%);
--accents-4: hsl(0, 1%, 81%);
--accents-5: hsl(0, 0%, 89%);
--accents-6: hsl(50, 21%, 95%);
--theme-0: hsl(210, 24%, 84%);
--theme-1: hsl(209, 28%, 39%);
@apply overflow-x-hidden;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
/* Remove Safari input shadow on mobile */
textarea,
input:matches([type='email'], [type='number'], [type='password'], [type='search'], [type='tel'], [type='text'], [type='url']) {
-webkit-appearance: none;
}
html {
height: 100%;
box-sizing: border-box;
touch-action: manipulation;
font-feature-settings: 'case' 1, 'rlig' 1, 'calt' 0;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
-moz-osx-font-smoothing: grayscale;
--webkit-text-size-adjust: none;
text-size-adjust: none;
scroll-behavior: smooth;
}
html,
body {
font-family: var(--font-sans);
text-rendering: optimizeLegibility;
::--webkit-font-smoothing: antialiased;
::--moz-osx-font-smoothing: grayscale;
background-color: var(--reddit-0);
color: var(--text-accents-6);
}
body {
position: relative;
min-height: 100%;
margin: 0;
scrollbar-width: none;
scrollbar-color: var(--primary-0) var(--primary-9); /* scroll thumb and track */
}
body::-webkit-scrollbar {
display: thin; /* Hide scrollbar for Chrome, Safari and Opera https://www.w3schools.com/howto/howto_css_hide_scrollbars.asp */
width: 10px;
}
body::-webkit-scrollbar-track {
background: var(--accents-7); /* color of the tracking area */
}
body::-webkit-scrollbar-thumb {
background-color: var(
--secondary-0
); /* color of the scroll thumb */
border-radius: 0px; /* roundness of the scroll thumb */
border: 3px var(--secondary-0); /* creates padding around scroll thumb */
}
a {
-webkit-tap-highlight-color: hsla(0, 0%, 0%, 0);
}
.animated {
--webkit-animation-duration: 1s;
--animation-duration: 1s;
-animation-duration: 1s;
--webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.fadeIn {
-webkit-animation-name: fadeIn;
animation-name: fadeIn;
}
@-webkit-keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
chrome-bug.css
/**
* Chrome has a bug with transitions onLoad since 2012!
*
* To prevent a "pop" of content, you have to disable all transitions until
* the page is done loading.
*
* https://lab.laukstein.com/bug/input
* https://twitter.com/timer150/status/1345217126680899584
*/
body.loading * {
transition: none !important;
}
The chrome-bug class is conditionally handled via a useEffect
hook in _app.tsx
and is called in the body of _document.tsx
_app.tsx
import '@/styles/index.css';
import '@/styles/chrome-bug.css';
import 'keen-slider/keen-slider.min.css';
import App, {
AppContext,
AppInitialProps,
AppProps,
NextWebVitalsMetric
} from 'next/app';
import { useRouter } from 'next/router';
import { ApolloProvider } from '@apollo/client';
import { useEffect, FC } from 'react';
import {
useApollo,
initializeApollo,
addApolloState
} from '@/lib/apollo';
import * as gtag from '@/lib/analytics';
import { MediaContextProvider } from '@/lib/artsy-fresnel';
import { Head } from '@/components/Head';
import { GTagPageview } from '@/types/analytics';
// import { AppLayout } from '@/components/Layout';
import {
DynamicNavQuery,
DynamicNavQueryVariables,
DynamicNavDocument,
MenuNodeIdTypeEnum
} from '@/graphql/generated/graphql';
const Noop: FC = ({ children }) => <>{children}</>;
function NextApp({
Component,
pageProps: { ...pageProps }
}: AppProps<typeof NextApp.getInitialProps>) {
const apolloClient = useApollo(pageProps);
const LayoutNoop = (Component as any).LayoutNoop || Noop;
const router = useRouter();
useEffect(() => {
document.body.classList?.remove('loading');
}, []);
useEffect(() => {
const handleRouteChange = (url: GTagPageview) => {
gtag.pageview(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
<>
<Head />
<ApolloProvider client={apolloClient}>
<MediaContextProvider>
<LayoutNoop pageProps={pageProps}>
<Component {...pageProps} />
</LayoutNoop>
</MediaContextProvider>
</ApolloProvider>
</>
);
}
NextApp.getInitialProps = async (
appContext: AppContext
): Promise<AppInitialProps> => {
const pageProps = await App.getInitialProps(appContext);
const graphqlClient = initializeApollo();
const dynamicNav = await graphqlClient.query<
DynamicNavQuery,
DynamicNavQueryVariables
>({
query: DynamicNavDocument,
variables: {
idHead: 'Header',
idTypeHead: MenuNodeIdTypeEnum.Name,
idFoot: 'Footer',
idTypeFoot: MenuNodeIdTypeEnum.Name
}
});
return addApolloState(graphqlClient, {
pageProps: {
...pageProps.pageProps,
Header: dynamicNav.data.Header,
Footer: dynamicNav.data.Footer
}
});
};
export function reportWebVitals(metric: NextWebVitalsMetric) {
console.debug('vital: ', metric);
}
export default NextApp;
_document.tsx
import Document, {
Head,
Html,
Main,
NextScript,
DocumentContext
} from 'next/document';
import { mediaStyles } from '@/lib/artsy-fresnel';
const GA_TRACKING_ID = 'G-RJQZB1C7TR';
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html lang='en-US'>
<Head>
<meta charSet='utf-8' />
<link
rel='stylesheet'
href='https://rsms.me/inter/inter.css'
/>
<link rel='shortcut icon' href='/assets/favicon.ico' />
<style
type='text/css'
dangerouslySetInnerHTML={{ __html: mediaStyles }}
/>
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`
}}
/>
</Head>
<body className='loading'>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
For the sake of thoroughness, here is my package.json
{
"name": "clean-fade",
"version": "1.0.0",
"main": "index.js",
"repository": "[email protected]:windy-city-devs-llc/clean-fade.git",
"author": "Andrew Ross <[email protected]>",
"license": "MIT",
"scripts": {
"test": "jest",
"test-all": "yarn lint && yarn type-check && yarn test",
"codegen": "graphql-codegen --config codegen.yml -r dotenv/config",
"dev": "next -p 5005",
"prod:build": "yarn codegen && yarn build",
"dev:debug": "cross-env NODE_OPTIONS='--inspect' next dev",
"build": "next build",
"analyze": "cross-env ANALYZE=true yarn build",
"find:unused": "next-unused",
"tsconfig:effective": "tsc --showConfig",
"pretty:quick": "yarn pretty-quick --staged",
"prepare": "husky install",
"lint": "eslint . --ext ts --ext tsx --ext js",
"format": "prettier --write .",
"type-check": "tsc --pretty --noEmit"
},
"lint-staged": {
"*.@(ts|tsx)": [
"yarn lint",
"yarn format"
]
},
"next-unused": {
"alias": {
"@/components/*": [
"components/*"
],
"@/config/*": [
"config/*"
],
"@/graphql/*": [
"graphql/*"
],
"@/lib/*": [
"lib/*"
],
"@/pages/*": [
"pages/*"
],
"@/scripts/*": [
"scripts/*"
],
"@/styles/*": [
"styles/*"
],
"@/test/*": [
"test/*"
],
"@/types/*": [
"types/*"
]
},
"debug": true,
"include": [
"components",
"lib",
"pages"
],
"exclude": [],
"entrypoints": [
"pages"
]
},
"dependencies": {
"@apollo/client": "^3.3.12",
"@artsy/fresnel": "^1.3.1",
"@headlessui/react": "^0.3.1",
"@reach/portal": "^0.13.2",
"body-scroll-lock": "^3.1.5",
"classnames": "^2.2.6",
"date-fns": "^2.19.0",
"graphql": "^15.5.0",
"html-react-parser": "^1.2.4",
"isomorphic-unfetch": "^3.1.0",
"js-cookie": "^2.2.1",
"keen-slider": "^5.4.0",
"lodash.random": "^3.2.0",
"lodash.throttle": "^4.1.1",
"next": "^10.0.9",
"next-seo": "^4.20.0",
"next-themes": "^0.0.12",
"preact": "^10.5.13",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-intersection-observer": "^8.31.0",
"react-markdown": "^5.0.3",
"react-merge-refs": "^1.1.0",
"react-tsparticles": "^1.20.1",
"remark-gfm": "^1.0.0",
"swr": "^0.5.3",
"tabbable": "^5.1.6"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@graphql-codegen/cli": "^1.21.3",
"@graphql-codegen/import-types-preset": "^1.18.1",
"@graphql-codegen/introspection": "^1.18.1",
"@graphql-codegen/near-operation-file-preset": "^1.17.13",
"@graphql-codegen/schema-ast": "^1.18.1",
"@graphql-codegen/typescript": "^1.21.1",
"@graphql-codegen/typescript-operations": "^1.17.15",
"@graphql-codegen/typescript-react-apollo": "2.1.1",
"@next/bundle-analyzer": "^10.0.9",
"@tailwindcss/aspect-ratio": "^0.2.0",
"@tailwindcss/forms": "^0.2.1",
"@tailwindcss/typography": "^0.4.0",
"@testing-library/dom": "^7.30.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3",
"@types/body-scroll-lock": "^2.6.1",
"@types/classnames": "^2.2.11",
"@types/gtag.js": "^0.0.4",
"@types/jest": "^26.0.20",
"@types/js-cookie": "^2.2.6",
"@types/lodash.random": "^3.2.6",
"@types/lodash.throttle": "^4.1.6",
"@types/node": "^14.14.35",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/react-test-renderer": "^17.0.1",
"@types/tabbable": "^3.1.0",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"autoprefixer": "^10.2.5",
"babel-jest": "^26.6.3",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"dotenv-cli": "^4.0.0",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-react": "^7.22.0",
"globby": "^11.0.2",
"husky": "^5.1.3",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest-watch-typeahead": "^0.6.1",
"lint-staged": "^10.5.4",
"next-page-tester": "^0.24.0",
"next-unused": "^0.0.3",
"postcss": "^8.2.8",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^14.0.0",
"postcss-preset-env": "^6.7.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.2.1",
"pretty-quick": "^3.1.0",
"react-test-renderer": "^17.0.1",
"stylelint": "^13.12.0",
"stylelint-config-recommended": "^4.0.0",
"tailwindcss": "^2.0.3",
"tailwindcss-line-clamp": "^1.0.5",
"ts-jest": "^26.5.3",
"typescript": "^4.2.3",
"yaml-loader": "^0.6.0"
}
}
Solution 3:[3]
Next.js, How to setup tailwindcss with purge css to remove unused css
- install tailwindcss
yarn add -D tailwindcss
npx tailwindcss init
- configure your tailwind.config.js
module.exports = {
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}'
],
theme: {
extend: {},
},
plugins: [],
}
- install purge css plugins
yarn add @fullhuman/postcss-purgecss postcss-preset-env
- configure postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@fullhuman/postcss-purgecss': {
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}'
],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
},
'postcss-preset-env': {}
},
}
After that try adding a classname that you intentionally won't use and try to build for production and test from coverage tab that this classname is not loaded within source file
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Daryn |
Solution 2 | |
Solution 3 | Mhd Louay Al-osh |