Next.js CSS 样式学习笔记

Next.js 支持多种 CSS 方案,从传统的 CSS Modules 到现代的 Tailwind CSS,再到 CSS-in-JS,开发者可以根据项目需求灵活选择。


1. 全局样式

1.1 基本用法

全局样式文件通常放在 app/globals.css,并在根布局中引入:

// app/layout.tsx
import './globals.css';

export default function RootLayout({ children }) {
  return (
    <html lang="zh">
      <body>{children}</body>
    </html>
  );
}
/* app/globals.css */
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background-color: #f5f5f5;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

1.2 CSS 变量(推荐)

Next.js 推荐使用 CSS 变量定义主题:

/* app/globals.css */
:root {
  --primary-color: #3b82f6;
  --secondary-color: #10b981;
  --text-color: #1f2937;
  --bg-color: #ffffff;
  --border-radius: 8px;
  --spacing-unit: 4px;
}

.dark {
  --primary-color: #60a5fa;
  --text-color: #f3f4f6;
  --bg-color: #111827;
}

body {
  color: var(--text-color);
  background-color: var(--bg-color);
}

.button {
  background-color: var(--primary-color);
  border-radius: var(--border-radius);
  padding: calc(var(--spacing-unit) * 3);
}

2. CSS Modules

CSS Modules 是 Next.js 的默认方案,自动将类名转换为唯一标识,避免样式冲突。

2.1 基本用法

// app/components/Button.tsx
import styles from './Button.module.css';

export default function Button({ children }) {
  return <button className={styles.button}>{children}</button>;
}
/* app/components/Button.module.css */
.button {
  background-color: #3b82f6;
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
}

.button:hover {
  background-color: #2563eb;
}

.button:active {
  transform: scale(0.98);
}

编译后的类名(自动转换):

<button class="Button_button__abc123">点击我</button>

2.2 组合类名

import styles from './Card.module.css';

export default function Card({ title, content }) {
  return (
    <div className={`${styles.card} ${styles.shadow}`}>
      <h2 className={styles.title}>{title}</h2>
      <p className={styles.content}>{content}</p>
    </div>
  );
}

2.3 动态类名

import styles from './Alert.module.css';

export default function Alert({ type = 'info', children }) {
  return (
    <div className={`${styles.alert} ${styles[type]}`}>
      {children}
    </div>
  );
}
/* Alert.module.css */
.alert {
  padding: 12px;
  border-radius: 8px;
}

.info {
  background-color: #dbeafe;
  color: #1e40af;
}

.warning {
  background-color: #fef3c7;
  color: #92400e;
}

.error {
  background-color: #fee2e2;
  color: #991b1b;
}

2.4 全局样式(CSS Modules 中)

/* Button.module.css */
.button {
  padding: 12px;
}

/* :global() 中的类名不会被转换 */
:global(.container) {
  max-width: 1200px;
  margin: 0 auto;
}

3. Tailwind CSS(推荐)

Tailwind CSS 是 Next.js 官方推荐的实用优先 CSS 框架,开发效率极高。

3.1 安装与配置

# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

3.2 基本用法

export default function Card({ title, content }) {
  return (
    <div className="max-w-md mx-auto bg-white rounded-lg shadow-md p-6">
      <h2 className="text-2xl font-bold text-gray-800 mb-4">{title}</h2>
      <p className="text-gray-600 leading-relaxed">{content}</p>
      <button className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
        了解更多
      </button>
    </div>
  );
}

3.3 响应式设计

export default function ResponsiveCard() {
  return (
    <div className="
      p-4
      sm:p-6
      md:p-8
      lg:p-12
      bg-white
      rounded-lg
      shadow-md
    ">
      <h1 className="
        text-xl
        sm:text-2xl
        md:text-3xl
        lg:text-4xl
        font-bold
      ">
        响应式标题
      </h1>
    </div>
  );
}

3.4 暗色模式

'use client';
import { useState } from 'react';

export default function DarkModeToggle() {
  const [darkMode, setDarkMode] = useState(false);

  return (
    <div className={darkMode ? 'dark' : ''}>
      <button
        onClick={() => setDarkMode(!darkMode)}
        className="px-4 py-2 bg-gray-200 dark:bg-gray-800 dark:text-white rounded"
      >
        {darkMode ? '☀️ 亮色' : '🌙 暗色'}
      </button>
    </div>
  );
}
// tailwind.config.js
module.exports = {
  darkMode: 'class',  // 使用 class 策略
  // ...
};

3.5 自定义配置

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
        },
      },
      spacing: {
        '128': '32rem',
      },
      fontFamily: {
        sans: ['Inter', 'sans-serif'],
      },
    },
  },
};
// 使用自定义配置
<div className="bg-primary-500 p-128 font-sans">
  自定义配置
</div>

4. CSS-in-JS

Next.js 支持多种 CSS-in-JS 库,但需要注意与服务端渲染的兼容性。

4.1 styled-jsx(内置)

Next.js 内置支持,无需额外安装。

export default function Card({ title }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <style jsx>{`
        .card {
          background-color: white;
          padding: 20px;
          border-radius: 8px;
          box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        h2 {
          color: #1f2937;
          font-size: 1.5rem;
          margin-bottom: 0.5rem;
        }
      `}</style>
    </div>
  );
}

全局样式

export default function Layout({ children }) {
  return (
    <div>
      {children}
      <style jsx global>{`
        body {
          margin: 0;
          font-family: -apple-system, BlinkMacSystemFont, sans-serif;
        }
      `}</style>
    </div>
  );
}

4.2 styled-components

npm install styled-components
'use client';
import styled from 'styled-components';

const Card = styled.div`
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

  h2 {
    color: #1f2937;
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
  }
`;

const Button = styled.button`
  background-color: #3b82f6;
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  border: none;
  cursor: pointer;

  &:hover {
    background-color: #2563eb;
  }
`;

export default function StyledCard({ title }) {
  return (
    <Card>
      <h2>{title}</h2>
      <Button>点击我</Button>
    </Card>
  );
}

服务端渲染配置

// app/layout.tsx
'use client';
import { ServerStyleSheet } from 'styled-components';
import { useServerInsertedHTML } from 'next/navigation';

export default function RootLayout({ children }) {
  useServerInsertedHTML(() => {
    const sheet = new ServerStyleSheet();
    const styles = sheet.getStyleElement();
    return <>{styles}</>;
  });

  return (
    <html lang="zh">
      <body>{children}</body>
    </html>
  );
}

4.3 Emotion

npm install @emotion/react @emotion/styled
'use client';
import styled from '@emotion/styled';

const Card = styled.div`
  background-color: white;
  padding: 20px;
  border-radius: 8px;
`;

export default function EmotionCard({ title }) {
  return <Card>{title}</Card>;
}

5. Sass / SCSS

Next.js 原生支持 Sass,无需额外配置。

npm install -D sass
// app/components/Button.module.scss
$primary-color: #3b82f6;
$hover-color: #2563eb;

.button {
  background-color: $primary-color;
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  border: none;
  cursor: pointer;

  &:hover {
    background-color: $hover-color;
  }

  &.large {
    padding: 16px 32px;
    font-size: 1.1rem;
  }
}
import styles from './Button.module.scss';

export default function Button({ children, size = 'normal' }) {
  return (
    <button className={`${styles.button} ${styles[size]}`}>
      {children}
    </button>
  );
}

6. next/font(字体优化)

Next.js 提供了 next/font 自动优化字体加载,避免布局偏移。

6.1 Google Fonts

// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
});

export default function RootLayout({ children }) {
  return (
    <html lang="zh" className={inter.variable}>
      <body>{children}</body>
    </html>
  );
}
/* app/globals.css */
body {
  font-family: var(--font-inter);
}

6.2 本地字体

// app/layout.tsx
import localFont from 'next/font/local';

const myFont = localFont({
  src: './fonts/MyFont.woff2',
  display: 'swap',
  variable: '--font-my-font',
});

export default function RootLayout({ children }) {
  return (
    <html lang="zh" className={myFont.variable}>
      <body>{children}</body>
    </html>
  );
}

6.3 多字体组合

import { Inter, Playfair_Display } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
});

const playfair = Playfair_Display({
  subsets: ['latin'],
  variable: '--font-playfair',
});

export default function RootLayout({ children }) {
  return (
    <html lang="zh" className={`${inter.variable} ${playfair.variable}`}>
      <body>{children}</body>
    </html>
  );
}
h1 {
  font-family: var(--font-playfair);
}

p {
  font-family: var(--font-inter);
}

7. next/image(图片优化)

Next.js 的 <Image> 组件自动优化图片,包括懒加载、格式转换、尺寸适配。

import Image from 'next/image';

export default function ImageExample() {
  return (
    <div>
      {/* 本地图片 */}
      <Image
        src="/images/logo.png"
        alt="Logo"
        width={200}
        height={100}
      />

      {/* 远程图片(需配置域名) */}
      <Image
        src="https://example.com/image.jpg"
        alt="远程图片"
        width={800}
        height={600}
        priority  // 首屏图片,优先加载
      />

      {/* 响应式图片 */}
      <Image
        src="/hero.jpg"
        alt="Hero"
        fill
        sizes="(max-width: 768px) 100vw, 50vw"
        style={{ objectFit: 'cover' }}
      />

      {/* 懒加载(默认) */}
      <Image
        src="/lazy.jpg"
        alt="懒加载图片"
        width={400}
        height={300}
        loading="lazy"
      />
    </div>
  );
}

配置外部图片域名

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
      {
        protocol: 'https',
        hostname: '**.cdn.example.com',
      },
    ],
  },
};

8. 样式方案对比

方案 优点 缺点 适用场景
全局 CSS 简单直接 容易冲突 小型项目、快速原型
CSS Modules 避免冲突、原生支持 类名冗长 中型项目、组件库
Tailwind CSS 开发快、体积小、一致性好 HTML 冗长、学习成本 大型项目、团队协作
styled-jsx 内置支持、作用域隔离 不如其他方案流行 简单组件
styled-components 动态样式强、生态好 包体积大、SSR 配置复杂 需要动态样式的项目
Emotion 轻量、性能好 SSR 配置复杂 性能敏感项目
Sass/SCSS 变量、嵌套、混入 需要编译 传统项目迁移

9. 最佳实践

9.1 样式组织

src/
├── app/
│   └── globals.css          # 全局样式、CSS 变量
├── components/
│   ├── Button/
│   │   ├── index.tsx
│   │   └── Button.module.css  # 组件样式
│   └── Card/
│       ├── index.tsx
│       └── Card.module.css
└── styles/
    ├── variables.css        # CSS 变量
    ├── mixins.css           # Sass 混入
    └── utilities.css        # 工具类

9.2 性能优化

// ✅ 使用 CSS Modules 或 Tailwind,避免运行时样式计算
import styles from './Button.module.css';

// ❌ 避免内联样式(除非是动态值)
<button style={{ backgroundColor: color }}>点击</button>

// ✅ 动态值用 CSS 变量
<button style={{ '--color': color } as React.CSSProperties}>点击</button>
/* Button.module.css */
.button {
  background-color: var(--color);
}

9.3 响应式设计

// ✅ 使用 Tailwind 的响应式前缀
<div className="p-4 sm:p-6 md:p-8 lg:p-12">
  响应式内容
</div>

// ✅ 使用 CSS 媒体查询
/* Card.module.css */
.card {
  padding: 16px;
}

@media (min-width: 768px) {
  .card {
    padding: 24px;
  }
}

9.4 暗色模式

// ✅ 使用 CSS 变量 + Tailwind
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
  暗色模式内容
</div>

// ✅ 使用 CSS 变量
:root {
  --bg-color: white;
  --text-color: black;
}

.dark {
  --bg-color: black;
  --text-color: white;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
}

10. 完整示例:响应式卡片组件

// app/components/Card.tsx
import Image from 'next/image';
import styles from './Card.module.css';

export default function Card({ title, description, image, tags }) {
  return (
    <article className={styles.card}>
      <div className={styles.imageWrapper}>
        <Image
          src={image}
          alt={title}
          fill
          sizes="(max-width: 768px) 100vw, 50vw"
          className={styles.image}
        />
      </div>
      <div className={styles.content}>
        <h2 className={styles.title}>{title}</h2>
        <p className={styles.description}>{description}</p>
        <div className={styles.tags}>
          {tags.map((tag) => (
            <span key={tag} className={styles.tag}>
              {tag}
            </span>
          ))}
        </div>
      </div>
    </article>
  );
}
/* Card.module.css */
.card {
  background-color: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  transition: transform 0.2s, box-shadow 0.2s;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}

.imageWrapper {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.image {
  object-fit: cover;
}

.content {
  padding: 20px;
}

.title {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
  color: #1f2937;
}

.description {
  color: #6b7280;
  line-height: 1.6;
  margin-bottom: 1rem;
}

.tags {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.tag {
  background-color: #e5e7eb;
  color: #374151;
  padding: 4px 12px;
  border-radius: 9999px;
  font-size: 0.875rem;
}

@media (min-width: 768px) {
  .content {
    padding: 24px;
  }

  .title {
    font-size: 1.75rem;
  }
}

总结

核心概念 要点
全局样式 app/globals.css + CSS 变量
CSS Modules 默认方案,自动类名转换,避免冲突
Tailwind CSS 官方推荐,实用优先,开发效率高
CSS-in-JS styled-components / Emotion,动态样式强
字体优化 next/font 自动优化,避免布局偏移
图片优化 <Image> 组件自动懒加载、格式转换
最佳实践 优先 CSS Modules / Tailwind,动态值用 CSS 变量

一句话总结:Next.js 样式方案的核心是模块化 + 性能优化——CSS Modules 避免冲突,Tailwind 提升效率,next/font<Image> 自动优化,CSS 变量实现动态主题。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐