跳到主要内容

样式方案

Next.js 支持多种样式方案,包括 CSS Modules、Tailwind CSS、CSS-in-JS 等。本章将介绍各种样式的使用方法。

CSS Modules

CSS Modules 是 Next.js 内置支持的样式方案,自动生成唯一的类名。

基本用法

// src/app/components/Button/Button.module.css
.button {
padding: 10px 20px;
background-color: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.button:hover {
background-color: #0051cc;
}

.primary {
background-color: #0070f3;
}

.secondary {
background-color: #6c757d;
}
// src/app/components/Button/Button.tsx
import styles from "./Button.module.css";

type Props = {
children: React.ReactNode;
variant?: "primary" | "secondary";
onClick?: () => void;
};

export default function Button({
children,
variant = "primary",
onClick,
}: Props) {
return (
<button
className={`${styles.button} ${styles[variant]}`}
onClick={onClick}
>
{children}
</button>
);
}

类名组合

使用 clsxclassnames 库组合类名:

import clsx from "clsx";
import styles from "./Button.module.css";

export default function Button({ variant, size }: Props) {
return (
<button
className={clsx(
styles.button,
styles[variant],
size === "large" && styles.large
)}
>
Click me
</button>
);
}

Tailwind CSS

Tailwind CSS 是一个实用优先的 CSS 框架,Next.js 开箱即用。

配置

创建项目时选择 Tailwind CSS,或手动安装:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

配置 tailwind.config.ts

import type { Config } from "tailwindcss";

const config: Config = {
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: [],
};

export default config;

基本用法

export default function Page() {
return (
<div className="min-h-screen bg-gray-100">
<header className="bg-white shadow">
<nav className="container mx-auto px-4 py-4">
<h1 className="text-xl font-bold text-gray-900">我的网站</h1>
</nav>
</header>

<main className="container mx-auto px-4 py-8">
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-2xl font-semibold mb-4">标题</h2>
<p className="text-gray-600">内容描述</p>
</div>
</main>
</div>
);
}

响应式设计

export default function ResponsiveLayout() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="p-4 bg-blue-100">卡片 1</div>
<div className="p-4 bg-green-100">卡片 2</div>
<div className="p-4 bg-yellow-100">卡片 3</div>
</div>
);
}

状态样式

export default function Button() {
return (
<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 active:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed">
点击我
</button>
);
}

自定义样式

export default function Card() {
return (
<div className="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md flex items-center space-x-4">
<div className="shrink-0">
<img className="h-12 w-12" src="/img/logo.svg" alt="Logo" />
</div>
<div>
<div className="text-xl font-medium text-black">标题</div>
<p className="text-slate-500">描述内容</p>
</div>
</div>
);
}

全局样式

添加全局样式

/* src/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-rgb: 255, 255, 255;
}

body {
color: rgb(var(--foreground-rgb));
background: rgb(var(--background-rgb));
font-family: system-ui, sans-serif;
}

a {
color: inherit;
text-decoration: none;
}

在布局中引入

// src/app/layout.tsx
import "./globals.css";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh-CN">
<body>{children}</body>
</html>
);
}

CSS 变量

使用 CSS 变量实现主题切换:

/* src/app/globals.css */
:root {
--primary-color: #0070f3;
--secondary-color: #6c757d;
--background: #ffffff;
--text-color: #333333;
}

[data-theme="dark"] {
--primary-color: #3291ff;
--secondary-color: #8a939c;
--background: #1a1a1a;
--text-color: #f0f0f0;
}

body {
background-color: var(--background);
color: var(--text-color);
}
// 使用 CSS 变量
export default function Button() {
return (
<button
style={{
backgroundColor: "var(--primary-color)",
color: "white",
}}
>
点击我
</button>
);
}

CSS-in-JS

Next.js 支持 CSS-in-JS 方案,如 styled-components、emotion 等。

styled-components

安装依赖:

npm install styled-components

配置:

// src/app/registry.tsx
"use client";

import React, { useState } from "react";
import { useServerInsertedHTML } from "next/navigation";
import { ServerStyleSheet, StyleSheetManager } from "styled-components";

export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode;
}) {
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());

useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});

if (typeof window !== "undefined") return <>{children}</>;

return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
);
}

使用:

import styled from "styled-components";

const Title = styled.h1`
font-size: 2rem;
color: #0070f3;
`;

const Container = styled.div`
padding: 20px;
background: #f5f5f5;
`;

export default function Page() {
return (
<Container>
<Title>Hello World</Title>
</Container>
);
}

样式最佳实践

组织结构

src/
├── app/
│ ├── globals.css
│ └── layout.tsx
└── components/
├── Button/
│ ├── Button.tsx
│ └── Button.module.css
└── Card/
├── Card.tsx
└── Card.module.css

命名约定

/* 使用 BEM 命名 */
.card {}
.card__header {}
.card__body {}
.card--featured {}

/* 或使用语义化命名 */
.container {}
.header {}
.content {}

条件样式

import clsx from "clsx";

export default function Alert({ type, children }: Props) {
return (
<div
className={clsx(
"p-4 rounded",
type === "success" && "bg-green-100 text-green-800",
type === "error" && "bg-red-100 text-red-800",
type === "warning" && "bg-yellow-100 text-yellow-800"
)}
>
{children}
</div>
);
}

小结

本章我们学习了:

  1. CSS Modules 的使用
  2. Tailwind CSS 的配置和用法
  3. 全局样式的添加
  4. CSS 变量的使用
  5. CSS-in-JS 方案
  6. 样式最佳实践

练习

  1. 使用 CSS Modules 创建一个按钮组件
  2. 使用 Tailwind CSS 创建一个响应式卡片布局
  3. 实现一个深色/浅色主题切换
  4. 创建一个可复用的 Alert 组件