Remix CSS模块 #

一、CSS模块概述 #

CSS Modules 提供了作用域隔离的CSS,避免样式冲突。Remix 原生支持 CSS Modules。

二、基本用法 #

2.1 创建CSS模块 #

创建 app/components/Button.module.css

css
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  font-weight: 500;
}

.primary {
  background-color: #3b82f6;
  color: white;
}

.secondary {
  background-color: #6b7280;
  color: white;
}

.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

2.2 使用CSS模块 #

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

interface ButtonProps {
  variant?: "primary" | "secondary";
  disabled?: boolean;
  children: React.ReactNode;
}

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

2.3 使用links函数加载样式 #

tsx
import type { LinksFunction } from "@remix-run/node";
import styles from "./Button.module.css?url";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: styles },
];

export function Button() {
  return <button className="button">点击</button>;
}

三、类名组合 #

3.1 使用clsx #

安装 clsx:

bash
npm install clsx

使用示例:

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

export function Button({ variant, size, disabled, children }) {
  return (
    <button
      className={clsx(
        styles.button,
        styles[variant],
        styles[size],
        disabled && styles.disabled
      )}
    >
      {children}
    </button>
  );
}

3.2 组合多个模块 #

tsx
import clsx from "clsx";
import buttonStyles from "./Button.module.css";
import textStyles from "./Text.module.css";

export function Button({ children }) {
  return (
    <button className={clsx(buttonStyles.button, textStyles.bold)}>
      {children}
    </button>
  );
}

四、CSS变量 #

4.1 定义变量 #

css
/* app/styles/variables.css */
:root {
  --color-primary: #3b82f6;
  --color-secondary: #6b7280;
  --color-danger: #ef4444;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --radius: 0.25rem;
}

4.2 使用变量 #

css
/* Button.module.css */
.button {
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius);
  background-color: var(--color-primary);
  color: white;
}

.danger {
  background-color: var(--color-danger);
}

4.3 加载全局样式 #

tsx
import type { LinksFunction } from "@remix-run/node";
import variables from "~/styles/variables.css?url";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: variables },
];

五、组件样式组织 #

5.1 目录结构 #

text
app/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.module.css
│   │   └── index.ts
│   └── Card/
│       ├── Card.tsx
│       └── Card.module.css
└── styles/
    ├── variables.css
    └── global.css

5.2 组件示例 #

tsx
import clsx from "clsx";
import type { LinksFunction } from "@remix-run/node";
import styles from "./Card.module.css?url";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: styles },
];

interface CardProps {
  title: string;
  variant?: "default" | "highlighted";
  children: React.ReactNode;
}

export function Card({ title, variant = "default", children }: CardProps) {
  return (
    <div className={clsx(styles.card, styles[variant])}>
      <h3 className={styles.title}>{title}</h3>
      <div className={styles.content}>{children}</div>
    </div>
  );
}

六、响应式样式 #

6.1 媒体查询 #

css
/* Card.module.css */
.card {
  padding: 1rem;
}

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

@media (min-width: 1024px) {
  .card {
    padding: 2rem;
  }
}

6.2 CSS Grid响应式 #

css
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

七、动画和过渡 #

7.1 过渡效果 #

css
.button {
  transition: all 0.2s ease-in-out;
}

.button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

7.2 动画 #

css
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card {
  animation: fadeIn 0.3s ease-out;
}

八、最佳实践 #

8.1 命名规范 #

css
/* 推荐:语义化命名 */
.container { }
.header { }
.content { }
.footer { }
.title { }
.description { }

/* 不推荐:外观命名 */
.blueBox { }
.bigText { }
.leftColumn { }

8.2 组合而非继承 #

css
/* 基础样式 */
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
}

/* 变体样式 */
.primary {
  background-color: #3b82f6;
  color: white;
}

.large {
  padding: 1rem 2rem;
  font-size: 1.125rem;
}

8.3 避免深层嵌套 #

css
/* 不推荐 */
.card .header .title .icon { }

/* 推荐 */
.card { }
.cardHeader { }
.cardTitle { }
.cardIcon { }

九、总结 #

本章我们学习了:

  1. 基本用法:创建和使用CSS模块
  2. 类名组合:使用clsx组合类名
  3. CSS变量:定义和使用全局变量
  4. 响应式:媒体查询和响应式布局
  5. 最佳实践:命名规范和代码组织

核心要点:

  • CSS模块提供作用域隔离
  • 使用clsx组合类名
  • 使用CSS变量管理主题
  • 保持样式简洁和语义化
最后更新:2026-03-28