0Pricing
Tailwind CSS Academy · 강의

CSS Modules와 Tailwind

CSS Modules 안에서 Tailwind 유틸리티를 사용하여 React 및 Next.js 앱에서 범위가 지정된 컴포넌트 스타일과 유틸리티 우선 방식을 결합합니다.

CSS Modules와 Tailwind은(는) CoddyKit의 무료 Tailwind CSS Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Tailwind CSS Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

What Are CSS Modules

CSS Modules are CSS files where class names are locally scoped by default. When you import a CSS Module into a JavaScript component, the class names are transformed into unique identifiers like Button_root__Xk2p1, preventing style leakage between components. CSS Modules are built into Next.js and available in most React setups via webpack or Vite.

/* Button.module.css */
.root {
  display: flex;
  align-items: center;
}

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

const Button = () => (
  // styles.root resolves to 'Button_root__Xk2p1' at runtime
  <button className={styles.root}>Click me</button>
);

Why Combine CSS Modules With Tailwind

Tailwind and CSS Modules seem philosophically opposed — Tailwind avoids naming things while CSS Modules is all about naming things locally. But they complement each other in practice. CSS Modules handle complex selectors, :focus-within chains, and pseudo-element styling that Tailwind cannot express easily. Tailwind handles the bulk of visual styling. Together they cover 100% of styling needs.

Using @apply Inside CSS Modules

The most common pattern for combining the two is using @apply inside a CSS Module file. You define a locally-scoped class in the .module.css file and compose it from Tailwind utilities using @apply. This gives you the scoping benefits of CSS Modules while keeping the styling expressed in Tailwind's vocabulary.

/* Card.module.css */
.card {
  @apply rounded-xl bg-white p-6 shadow-sm;
}

.cardHeader {
  @apply border-b border-gray-100 pb-4 text-lg font-semibold text-gray-900;
}

.cardBody {
  @apply pt-4 text-sm text-gray-600 leading-relaxed;
}

Importing and Using Module Classes

Import the CSS Module in the React component and reference the local class names through the imported object. Combine module class names with global Tailwind utilities in the className prop using string interpolation or clsx. Module classes handle structure, Tailwind utilities handle responsive or state-specific overrides directly in JSX.

import styles from './Card.module.css';
import { clsx } from 'clsx';

interface CardProps {
  highlighted?: boolean;
  children: React.ReactNode;
}

export function Card({ highlighted, children }: CardProps) {
  return (
    <div
      className={clsx(
        styles.card,
        // Tailwind utilities alongside module classes
        highlighted && 'ring-2 ring-blue-500',
        'transition hover:shadow-md'
      )}
    >
      {children}
    </div>
  );
}

CSS Modules for Complex Selectors

Use CSS Modules specifically when Tailwind cannot express a selector. For example, styling a child element based on parent state with a complex selector, or using ::before and ::after pseudo-elements for decorative content that cannot be achieved with ring or shadow utilities. The .module.css file is the right home for these exceptions.

/* Dropdown.module.css */
/* Tailwind cannot easily target this nested state */
.trigger:focus-within .menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.menu {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-8px);
  transition: opacity 150ms ease, transform 150ms ease;
}

/* Decorative pseudo-element */
.triangle::after {
  content: '';
  position: absolute;
  border: 6px solid transparent;
  border-bottom-color: white;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
}

Composing Module Classes

CSS Modules support the composes keyword to inherit styles from another class within the same file or a different module file. This is CSS Modules' native approach to reuse. While @apply brings in Tailwind utilities, composes brings in other module classes — you can combine both in the same file for maximum flexibility.

/* base.module.css */
.baseButton {
  @apply inline-flex items-center rounded-lg px-4 py-2 font-semibold
         transition focus:outline-none focus:ring-2 focus:ring-offset-2;
}

/* Button.module.css */
.primary {
  composes: baseButton from './base.module.css';
  @apply bg-blue-600 text-white focus:ring-blue-500 hover:bg-blue-700;
}

.secondary {
  composes: baseButton from './base.module.css';
  @apply border border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-400;
}

CSS Modules in Next.js

Next.js supports CSS Modules out of the box with zero configuration — any file ending in .module.css is automatically scoped. The PostCSS pipeline that processes Tailwind also processes CSS Modules, so @apply works inside module files without any additional setup. This makes Next.js the ideal environment for the Tailwind + CSS Modules combination.

// next.config.js — no extra config needed
// CSS Modules and Tailwind work automatically

// Any .module.css file is locally scoped:
// components/Hero/Hero.module.css  ← scoped
// styles/globals.css               ← global (Tailwind entry)

// Verify your postcss.config.js has tailwindcss:
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

When NOT to Use CSS Modules

CSS Modules add a file per component and a layer of indirection that is overkill for most Tailwind components. Avoid them for simple components where all styling fits comfortably in className with utilities. Reserve CSS Modules for components with genuinely complex CSS that exceeds what utility classes can express — this is usually less than 10% of components in a typical Tailwind project.

// DON'T: CSS Module for a simple button — overkill
/* Button.module.css */
/* .btn { @apply px-4 py-2 rounded bg-blue-600 text-white; } */

// DO: Just use classes directly — much simpler
const Button = ({ children }) => (
  <button className="rounded bg-blue-600 px-4 py-2 font-semibold text-white
                     transition hover:bg-blue-700">
    {children}
  </button>
);

Global vs Local CSS in Next.js

In Next.js, only global stylesheets (globals.css) can be imported in _app.js or layout.tsx. CSS Modules are imported only inside the component that uses them. This constraint is intentional — it prevents accidental global style leakage. Tailwind's global directives always live in globals.css; module files contain only component-scoped rules.

// app/layout.tsx — import globals (Tailwind entry) here
import '../styles/globals.css';     // ✅ global CSS
// import './Card.module.css';      // ❌ Error: CSS Modules not allowed here

// components/Card.tsx — import module CSS here
import styles from './Card.module.css'; // ✅ scoped to this file

export function Card() {
  return <div className={styles.card}>...</div>;
}

Debugging CSS Module Class Names

In development mode, CSS Module class names include the file name and original class name for readability: Card_root__Xk2p1. In production, they are shortened to opaque hashes for size. If a module class is not applying, inspect the element in browser DevTools to see the generated class name and confirm it appears in the element's class list. Check for typos in the property access: styles.cardHeader not styles.card-header (camelCase required).

/* Card.module.css */
.card-header { /* ← hyphenated CSS class */ }

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

// Accessing hyphenated classes requires bracket notation:
const el = <div className={styles['card-header']}>...</div>;

// Better: use camelCase in the module file:
/* .cardHeader { } */
const el2 = <div className={styles.cardHeader}>...</div>;

TypeScript Support for CSS Modules

TypeScript does not know the shape of CSS Module imports by default, causing styles.anyClass to type-check as any. Install typescript-plugin-css-modules or use typed-css-modules to generate .d.ts type declarations for each module file. This gives autocomplete for class names in your editor and catches typos at compile time.

npm install -D typescript-plugin-css-modules

// tsconfig.json
{
  "compilerOptions": {
    "plugins": [
      { "name": "typescript-plugin-css-modules" }
    ]
  }
}

// Now styles.cardHeader shows autocomplete in VS Code
// and TypeScript warns if you access a class that does not exist

Quick Check

Test your understanding of Tailwind CSS Mastery concepts from this lesson.

Lesson Recap

In this lesson you learned: using @apply inside CSS Modules to combine scoped class names with Tailwind utilities, when to reach for CSS Modules versus staying in className (complex selectors and pseudo-elements), and TypeScript support for type-safe CSS Module class access. Next up we explore scaling Tailwind across monorepos.

자주 묻는 질문

“CSS Modules와 Tailwind” 강의는 무료인가요?

네 — “CSS Modules와 Tailwind” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Tailwind CSS Academy 강의 전체를 잠금 해제할 수 있습니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“CSS Modules와 Tailwind”에서 뭘 배우나요?

CSS Modules 안에서 Tailwind 유틸리티를 사용하여 React 및 Next.js 앱에서 범위가 지정된 컴포넌트 스타일과 유틸리티 우선 방식을 결합합니다. 브라우저에서 직접 실행하는 실습 코드로 Tailwind CSS Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Tailwind CSS Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Tailwind CSS Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“CSS Modules와 Tailwind” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Tailwind CSS Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Tailwind CSS Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 파일 및 폴더 구성
  2. 레거시 CSS와 함께 사용하기
  3. CSS Modules와 Tailwind
  4. 모노레포 전반으로 Tailwind 확장하기
← Tailwind CSS Academy(으)로 돌아가기