The main content

  1. Css(Scss) global and modular;
  2. Import Scss files using aliases
  3. Support Arabic, etc. from right to left display;
  4. Px automatically changes to REM.
  5. Babel-plugin-react-css-modules simplifies modular class writing
  6. The classNames plug-in simplifies className string concatenation

The development environment

Windows 10;

Editor PhpStorm;

Node version 12.16.2;

Next, js version 9.4.4

Project initialization

[Next-.js] initializes and enables TypeScript

CSS(SCSS) global and modular

Next. Js built-in scheme

The Next. Js official documentation gives the usage of global and module files (9.3+) :

  • The global file is imported in the pages/_app.js file (error if imported elsewhere);
  • Module file to. The module. (CSS | SCSS | sass), can be introduced in any module;
  • Module files already have scope functionality built in. Each class will be assigned a unique ID without worrying about naming conflicts.

There are two problems that cannot be solved with the above solution:

  1. In order to facilitate code reuse, some underlying components do not want to do scope conversion and can only be introduced as global code in pages/_app.js file, resulting in component code and style code referenced in different places, which is difficult to maintain.

  2. Global variables and mixins must be manually imported in every file used, too cumbersome.

To solve the above problems, two plug-ins were introduced

@zeit/next-sass: Solve global and modular problems

sass-resources-loader: Solve global variables and mixins;

To avoid collisions, the script automatically disables built-in modularity when it detects @zeit/ Next-sass configuration, so compatibility issues are not a concern.

You will see the following prompt on the command line:

Warning: Built-in CSS support is being disabled due to custom CSS configuration being detected.
Copy the code

Installing a plug-in

npm install --save-dev @zeit/next-sass node-sass sass-resources-loader
Copy the code

Add the configuration

Create a new file, next-config.js

const withSass = require('@zeit/next-sass');

module.exports = withSass({
    // Enable the CSS modularization function
    cssModules: true.cssLoaderOptions: {
        importLoaders: 1.// scoped class format
        localIdentName: "[local]__[hash:base64:5]",},webpack: (config) = > {
        // Global variables and mixins
        config.module.rules.push({
            enforce: 'pre'.test: /.scss$/,
            loader: 'sass-resources-loader'.options: {
                resources: ['./components/styles/variables.scss']}});returnconfig; }});Copy the code

The test code

Global variables: the components/styles/variables. SCSS

$color: #56ad6a;
Copy the code

Global style: components/styles/global SCSS

:global { .global-container { margin: 0 auto; color: $color; }}Copy the code

Module styles: components/Example/style.css. SCSS

.local-container {
    border: solid 1px $color;
    width: 300px;
    height: 300px;
    text-align: left;
}
Copy the code

Module components: components/Example/index. The TSX

import styles from './style.scss';

export default function Example() {
    return <div className={'global-container'+styles['local-container']} > Test Scss </div>
}
Copy the code

Page component: pages/index.js rename to pages/index.tsx, modify the code (same as in initialization, no change required)

import Example from '.. /components/Example/index';

export default function Home() {
  return <Example/>
}
Copy the code

Introduce a global style and create pages/_app.tsx

import '.. /components/styles/global.scss';

function MyApp({ Component, pageProps }) {
    return <Component {. pageProps} / >
}

export default MyApp
Copy the code

Restart the project, view the elements, and observe the class changes

Pit: Editor error

The code compiles without problems on the command line or in the browser, but an error message appears in the editor

TS2307: Cannot find module './style.scss' or its corresponding type declarations.
Copy the code

Add the file declarations. D. ts

declare module '*.scss' {
    const content: {[className: string] :string};
    export = content;
}
Copy the code

Tsconfig. json include array add configuration, resolve ~

{
  "include": [
    "declarations.d.ts"]}Copy the code

Import SCSS files using aliases

Modify tsconfig.json according to the official documentation, add an alias, @ to the root directory, @@ to the Components directory

{
  "compilerOptions": {
    "baseUrl": "."."paths": {
      "@ / *": [". / *"]."@ @ / *": ["components/*"]}... }Copy the code

Change the import path in _app. TSX and restart.

import '@@/styles/global.scss';
Copy the code

Arabic language support is displayed from right to left

Use the PostCSS plug-inpostcss-rtl.

  1. Create the postcss.config.js file in the root directory

  2. Copy the default configuration in next.js (official documentation) and install the plugin NPM I -D postCSs-Flexbugs -fixes PostCSs-PRESET -env that is involved in the default configuration

    module.exports = {
        plugins: {
            'postcss-flexbugs-fixes': {},
            'postcss-preset-env': {
                autoprefixer: {
                    flexbox: 'no-2009',},stage: 3.features: {
                    'custom-properties': false,},},},}Copy the code
  3. After NPM I -d postCSS-rtl is installed, the postCSs-rtl configuration is added to the plugins object

    module.exports = {
        plugins: {
            'postcss-rtl': {},
            // 'postcss-flexbugs-fixes'...}}Copy the code
  4. Add pages/_document.tsx and add dir attributes to HTML elements

    import Document, { Html, Head, Main, NextScript } from 'next/document'
    
    class MyDocument extends Document {
        static async getInitialProps(ctx) {
            const initialProps = await Document.getInitialProps(ctx)
            return { ...initialProps }
        }
    
        render() {
            return (
                <Html dir="rtl">
                    <Head />
                    <body>
                    <Main />
                    <NextScript />
                    </body>
                </Html>)}}export default MyDocument;
    Copy the code
  5. View elements and observe class changes

Turn the px rem

Use the PostCSS plug-inpostcss-pxtorem.

  1. NPM I -d postCSS-pxtorem install plugin;

  2. Add configuration items to postcss.config.js

    module.exports = { plugins: { 'postcss-rtl': {}, 'postcss-pxtorem': { rootValue: 22, //1rem=22px, HTML element font size in design /rootValue= REM value after conversion unitPrecision: 5, // Reserved decimal point number after conversion propList: ['*'], // The attribute to convert mediaQuery: false, // Whether to convert @media condition px (condition only, not code block) minPixelValue: Exclude: /node_modules/ I}, //' postcss-flexbug-fixes '... }}Copy the code
  3. Sets the font size of the HTML element

    A new public/static/CSS/reset CSS, copy the code below, according to the actual site adapt to adjust the font and font size – the width of the size values.

    html {font-size: 11px; }@media (min-width: 320px) {html{font-size: 9.3867 px.;} }
    @media (min-width: 360px) {html{font-size: 10.5600 px.;} }
    @media (min-width: 375px) {html{font-size: 11px;} }
    @media (min-width: 384px) {html{font-size: 11.2640 px.;} }
    @media (min-width: 414px) {html{font-size: 12.1440 px.;} }
    @media (min-width: 448px) {html{font-size: 13.1413 px.;} }
    @media (min-width: 480px) {html{font-size: 14.0800 px.;} }
    @media (min-width: 512px) {html{font-size: 15.0187 px.;} }
    @media (min-width: 544px) {html{font-size: 15.9573 px.;} }
    @media (min-width: 576px) {html{font-size: 16.8960 px.;} }
    @media (min-width: 608px) {html{font-size: 17.8347 px.;} }
    @media (min-width: 640px) {html{font-size: 18.7733 px.;} }
    @media (min-width: 750px) {html{font-size: 22px;} }
    Copy the code

    The CSS file is imported into the pages/_document. TSX file

    <Head>
        <link rel="stylesheet" type="text/css" href="/static/css/reset.css"/>
    </Head>
    Copy the code
  4. The CSS code above can cover most devices, but if you need more precise matching, use JS.

    Create a new public/static/js/rem.js file, copy the following code, and adjust the values in the code according to the appropriate width and font size for the site:

    (function() {
        function addEvent(domElem, eventName, func, useCapture) {
            useCapture = typeofuseCapture ! = ='undefined' ? useCapture : false;
            domElem.addEventListener(eventName, func, useCapture);
        }
    
        function setHtmlFontSize() {
            var minSiteWidth = 320,
                maxSiteWidth = 750,
                maxFontSize = 22;
            var windowWidth = window.innerWidth ? window.innerWidth : document.documentElement.offsetWidth;
            windowWidth = Math.min(Math.max(windowWidth, minSiteWidth), maxSiteWidth);
            let fs = ~~(windowWidth * maxFontSize / maxSiteWidth);
            let tagHtml = document.getElementsByTagName('html') [0];
            tagHtml.style.cssText = 'font-size: ' + fs + 'px';
            let realfz = ~~(+window.getComputedStyle(tagHtml).fontSize.replace('px'.' ') * 10000) / 10000;
            if(fs ! == realfz) { tagHtml.style.cssText ='font-size: ' + fs * (fs / realfz) + 'px';
            }
        }
    
        setHtmlFontSize()
        addEvent(window.'resize', setHtmlFontSize)
    })();
    Copy the code

    The js is imported into the pages/_document.tsx file

    <Head>
        <script src="/static/js/rem.js"/>
    </Head>
    Copy the code
  5. View elements and observe class changes; Zoom window, test text width height has not achieved proportional zoom.

Babel-plugin-react-css-modules simplifies modular class writing

The CSS class for all projects in the company specifies how to use lowercase dashes.

In the example above, because the class has a dash (” local-container “), the reference in the component can only be className={styles[‘local-container’]}. To simplify writing, Introduce babel-plugin-react-CSS-modules.

Basic usage

Installing a plug-in

npm i -D babel-plugin-react-css-modules postcss-scss
Copy the code

Add the file.babelrc as configured in the plug-in documentation

{
  "plugins": [["react-css-modules", {
      "generateScopedName": "[local]__[hash:base64:5]"."exclude": "node_modules"."filetypes": {
        ".scss": {
          "syntax": "postcss-scss"}}}]]}Copy the code

Modify the component code components/Example/index. The TSX

import './style.scss';

export default function Example() {
    return <div className="global-container" style="local-container"> Test Scss </div>
}
Copy the code

An error was reported after running the code

error - ./pages/_app.tsx 7:9
Module parse failed: Unexpected token (7:9)
File was processed with these loaders:
 * ./node_modules/@next/react-refresh-utils/loader.js
 * ./node_modules/next/dist/build/webpack/loaders/next-babel-loader.js
You may need an additional loader to handle the result of these loaders.
|   pageProps
| }) {
>   return <Component {...pageProps} />;
| }
Copy the code

Refer to the Next. Js document Customizing Babel Config to modify the configuration

{
  "presets": [["next/babel"]],"plugins": [["react-css-modules", {
      "generateScopedName": "[local]__[hash:base64:5]"."exclude": "node_modules"."filetypes": {
        ".scss": {
          "syntax": "postcss-scss"}}}]]}Copy the code

An error was reported. Aliases could not be resolved

error - ./pages/_app.tsx
Error: D:\workspace\ava\nextjs-css\pages\_app.tsx: Cannot find module '@@/styles/global.scss'
Copy the code

Solutions:

  1. The installation

    npm i -D babel-plugin-module-resolver
    Copy the code
  2. Modify babelrc,

    {..."plugins": [["module-resolver", {
          "root": [". /"]."alias": {
            "@": "."."@ @": "./components"}}],... ] }Copy the code

Look at the element, the module does scope, the styleName attribute is removed, and the className merge is implemented, but! CSS didn’t! (Later verification found that the Mac was fine, only Windows had this problem.)

Solutions:

  1. Installing a plug-in

    npm i -D generic-names
    Copy the code
  2. Add a file named/generateScopedName. Js

    const path = require('path');
    const genericNames = require('generic-names');
    
    const generate = genericNames('[local]__[hash:base64:5]', {
        context: process.cwd()
    });
    
    const generateScopedName = (localName, filePath) = > {
        var relativePath = path.relative(process.cwd(), filePath);
        return generate(localName, relativePath);
    };
    module.exports = generateScopedName;
    Copy the code
  3. Modify the next. Config. Js

    const generateScopedName = require('./build/generateScopedName');
    
    module.exports = withSass({
        // Enable the CSS modularization function
        cssModules: true.cssLoaderOptions: {
            importLoaders: 1.// scoped class format localIdentName
            // localIdentName: "[local]__[hash:base64:5]",
            getLocalIdent: (context, localIdentName, localName) = > {
                return generateScopedName(localName, context.resourcePath)
            }
        },
        / /...
    });
    Copy the code
  4. Babel-plugin-react-css-modules Hash problem babel-plugin-react-CSs-modules hash problem

Pit: Editor error indicating that styleName cannot be properly resolved

The components/Example/index. There is an error in the TSX hints

TS2322: Type '{ children: string; className: string; styleName: string; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.   Property 'styleName' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.
Copy the code

@types/ react-CSS-modules

npm install --save-dev @types/react-css-modules
Copy the code

The classNames plug-in simplifies className string concatenation

When a DOM tag contains a dynamic class, this is cumbersome. For example, click global-container to switch colors:

Components/styles/variables. SCSS add global variable

$colorRed: #dd0000;
Copy the code

The components/styles/global SCSS, add the global style

:global {
    // ...
    .global-red {
        color: $colorRed; }}Copy the code

The components/Example/style.css. SCSS, add module style

.local-red {
    border-color: $colorRed;
}
Copy the code

Modify the components/Example/index. The TSX, using string concatenation, reading is very difficult, and easy to leak Spaces:

import { useState } from 'react';
import styles from './style.scss';

export default function Example() {
    const [isRed, setIsRed] = useState(false);

    return (
        <div
            className={'global-container'+styles['local-container'] + (isRed ? ' global-red'+styles['local-red'] :"')}onClick={()= >setIsRed(! isRed)} > Toggle Color</div>)}Copy the code

Another common way to write this is to stuff all the classes into an array and convert the array to a string

export default function Example() {
    const [isRed, setIsRed] = useState(false);

    const classes = ['global-container', styles['local-container']].if (isRed) {
        classes.push('global-red');
        classes.push(styles['local-red']);
    }

    return (
        <div
            className={classes.join("')}onClick={()= >setIsRed(! isRed)} > Toggle Color</div>)}Copy the code

Using the plug-in classnames simplifies writing as follows

import { useState } from 'react';
import classnames from 'classnames/bind';
import styles from './style.scss';

const cx = classnames.bind(styles);

export default function Example() {
    const [isRed, setIsRed] = useState(false);

    return (
        <div
            className={cx(
                'global-container'['local-container'] and {'global-red': isRed}, {'local-red': isRed})}onClick={()= >setIsRed(! isRed)} > Toggle Color</div>)}Copy the code

Babel-plugin-react-css-modules styleName is used in combination with styleName.

Reading the classnames (classnames/index.js) source code, the plug-in does only one thing: combine all the parameters into strings. StyleName is also theoretically available, so test it out

import { useState } from 'react';
import cn from 'classnames';
import './style.scss';

export default function Example() {
    const [isRed, setIsRed] = useState(false);

    return (
        <div
            className={cn(
                'global-container'{'global-red': isRed})}styleName={cn(
                'local-container'{'local-red': isRed})}onClick={()= >setIsRed(! isRed)} > Toggle Color</div>)}Copy the code

Check the page, it makes perfect sense

This is the easiest way to use it so far. If you have a better one, feel free to add it in the comments