样式方案
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>
);
}
类名组合
使用 clsx 或 classnames 库组合类名:
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>
);
}
小结
本章我们学习了:
- CSS Modules 的使用
- Tailwind CSS 的配置和用法
- 全局样式的添加
- CSS 变量的使用
- CSS-in-JS 方案
- 样式最佳实践
练习
- 使用 CSS Modules 创建一个按钮组件
- 使用 Tailwind CSS 创建一个响应式卡片布局
- 实现一个深色/浅色主题切换
- 创建一个可复用的 Alert 组件