Claude Code Plugins

Community-maintained marketplace

Feedback

|

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name css-modules
description CSS Modules with Lightning CSS and PostCSS for component-scoped styling. Covers *.module.css patterns, TypeScript integration, Vite configuration, and composition. Use when building complex animations, styling third-party components, or migrating legacy CSS.

CSS Modules

Overview

CSS Modules provide locally-scoped CSS by automatically generating unique class names at build time. This prevents style conflicts and enables true component encapsulation.

When to Use CSS Modules

Use Case CSS Modules Tailwind
Complex animations Best Good
Third-party component styling Best Harder
Legacy CSS migration Best Refactor needed
Rapid prototyping Slower Best
Design system utilities Not ideal Best
Component encapsulation Best N/A
Team with CSS expertise Best Either

Hybrid Approach: Use both together - Tailwind for utilities, CSS Modules for complex components.


Documentation Index

Vite Integration

Topic URL Description
CSS Modules in Vite https://vite.dev/guide/features#css-modules Vite's built-in support
Lightning CSS https://vite.dev/guide/features#lightning-css Fast CSS transforms
PostCSS https://vite.dev/guide/features#postcss PostCSS configuration

Lightning CSS

Topic URL Description
Documentation https://lightningcss.dev/docs.html Official docs
CSS Modules https://lightningcss.dev/css-modules.html Module support
Transpilation https://lightningcss.dev/transpilation.html Browser targeting
Bundling https://lightningcss.dev/bundling.html CSS bundling

CSS Modules Spec

Topic URL Description
CSS Modules https://github.com/css-modules/css-modules Specification
Composition https://github.com/css-modules/css-modules#composition Composing classes
Scoping https://github.com/css-modules/css-modules#naming Local vs global

TypeScript Integration

Topic URL Description
typed-css-modules https://github.com/Quramy/typed-css-modules Generate .d.ts files
vite-plugin-css-modules-dts https://github.com/mrcjkb/vite-plugin-css-modules-dts Vite plugin

CSS Modules Fundamentals

File Naming Convention

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.module.css      # CSS Module
│   │   └── Button.test.tsx
│   └── Card/
│       ├── Card.tsx
│       ├── Card.module.css        # CSS Module
│       └── index.ts

Files ending in .module.css are automatically processed as CSS Modules.

Basic Usage

/* Button.module.css */
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  font-weight: 500;
  transition: background-color 150ms ease;
}

.primary {
  background-color: hsl(221, 83%, 53%);
  color: white;
}

.primary:hover {
  background-color: hsl(224, 76%, 48%);
}

.secondary {
  background-color: hsl(0, 0%, 96%);
  color: hsl(0, 0%, 9%);
}
// Button.tsx
import styles from './Button.module.css'

interface ButtonProps {
  variant?: 'primary' | 'secondary'
  children: React.ReactNode
}

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

Generated Class Names

<!-- Input -->
<button class="${styles.button} ${styles.primary}">

<!-- Output (generated) -->
<button class="Button_button_x7d9f Button_primary_a3k2j">

Local vs Global Scope

/* Local by default */
.button {
  /* Generates: Button_button_hash */
}

/* Explicit local */
:local(.button) {
  /* Same as above */
}

/* Global (escape hatch) */
:global(.external-library-class) {
  /* Kept as-is: .external-library-class */
}

/* Global within local */
.card :global(.markdown-body) {
  /* Scoped parent, global child */
}

Composition

composes Keyword

Share styles between classes:

/* base.module.css */
.flexCenter {
  display: flex;
  align-items: center;
  justify-content: center;
}

.interactive {
  cursor: pointer;
  transition: all 150ms ease;
}
/* Button.module.css */
.button {
  composes: flexCenter from './base.module.css';
  composes: interactive from './base.module.css';
  padding: 0.5rem 1rem;
}

Multiple Compositions

.primaryButton {
  composes: button;
  composes: primary from './colors.module.css';
  composes: rounded from './shapes.module.css';
}

Usage in React

// Composed class automatically includes all composed classes
<button className={styles.primaryButton}>
  {/* Renders: Button_primaryButton_x Button_button_y colors_primary_z shapes_rounded_w */}
</button>

Vite Configuration

Lightning CSS (Recommended)

Lightning CSS is 100x faster than PostCSS for transforms:

npm install lightningcss browserslist
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import browserslistToTargets from 'lightningcss/browserslist'
import browserslist from 'browserslist'

export default defineConfig({
  plugins: [react()],
  css: {
    transformer: 'lightningcss',
    lightningcss: {
      targets: browserslistToTargets(browserslist('>= 0.25%')),
      cssModules: {
        // Class name pattern
        pattern: '[name]__[local]_[hash:5]',
        // Or for production:
        // pattern: '[hash:8]'
      }
    }
  },
  build: {
    cssMinify: 'lightningcss'
  }
})

Class Name Patterns

Pattern Example Output
[name]__[local]_[hash:5] Button__primary_a3k2j
[local]_[hash:8] primary_a3k2j9x1
[hash:8] a3k2j9x1 (production)

Lightning CSS Features (Included)

Lightning CSS automatically handles:

  • Vendor prefixing (replaces autoprefixer)
  • Modern syntax transpilation
  • Nesting
  • Custom media queries
  • Color functions (oklch, lab, lch)

PostCSS Configuration (When Needed)

For plugins Lightning CSS doesn't support:

// postcss.config.js
export default {
  plugins: {
    'postcss-import': {},
    'postcss-custom-media': {},
    // Don't use: autoprefixer (Lightning CSS handles this)
    // Don't use: postcss-nested (Lightning CSS handles this)
  }
}

Note: Use Lightning CSS for transforms, PostCSS only for unsupported plugins.


TypeScript Integration

Type Declarations

Without types, TypeScript doesn't know the shape of CSS modules:

// This would error without declarations
import styles from './Button.module.css'
styles.button  // TS error: Property 'button' does not exist

Option 1: Wildcard Declaration (Simple)

// src/vite-env.d.ts or src/types/css-modules.d.ts
declare module '*.module.css' {
  const classes: { [key: string]: string }
  export default classes
}

Pros: No extra tooling Cons: No autocomplete, no type safety

Option 2: Generated Declarations (Recommended)

npm install -D vite-plugin-css-modules-dts
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import cssModulesDts from 'vite-plugin-css-modules-dts'

export default defineConfig({
  plugins: [
    react(),
    cssModulesDts({
      // Generate .d.ts next to .module.css files
      outputDir: '.',
    })
  ]
})

Generated files:

// Button.module.css.d.ts (auto-generated)
declare const styles: {
  readonly button: string
  readonly primary: string
  readonly secondary: string
}
export default styles

Pros: Full autocomplete, type safety, catches typos Cons: Generated files in source (add to .gitignore)

Option 3: CLI Generation

npm install -D typed-css-modules

# Generate declarations
npx tcm src --pattern '**/*.module.css'

# Watch mode
npx tcm src --pattern '**/*.module.css' --watch

Add to package.json:

{
  "scripts": {
    "css:types": "tcm src --pattern '**/*.module.css'",
    "css:types:watch": "tcm src --pattern '**/*.module.css' --watch"
  }
}

Patterns

Component-Scoped Styling

/* Card.module.css */
.card {
  border-radius: 0.5rem;
  background: white;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.header {
  padding: 1rem;
  border-bottom: 1px solid hsl(0, 0%, 90%);
}

.content {
  padding: 1.5rem;
}

.footer {
  padding: 1rem;
  background: hsl(0, 0%, 98%);
}
// Card.tsx
import styles from './Card.module.css'

export function Card({ children }: { children: React.ReactNode }) {
  return <div className={styles.card}>{children}</div>
}

Card.Header = ({ children }: { children: React.ReactNode }) => (
  <header className={styles.header}>{children}</header>
)

Card.Content = ({ children }: { children: React.ReactNode }) => (
  <div className={styles.content}>{children}</div>
)

Card.Footer = ({ children }: { children: React.ReactNode }) => (
  <footer className={styles.footer}>{children}</footer>
)

CSS Variables with Modules

/* theme.css (global) */
:root {
  --color-primary: hsl(221, 83%, 53%);
  --color-primary-dark: hsl(224, 76%, 48%);
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
}

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

.button:hover {
  background-color: var(--color-primary-dark);
}

Theming with CSS Variables

/* theme.module.css */
.light {
  --bg: white;
  --text: hsl(0, 0%, 9%);
  --border: hsl(0, 0%, 90%);
}

.dark {
  --bg: hsl(0, 0%, 9%);
  --text: hsl(0, 0%, 98%);
  --border: hsl(0, 0%, 20%);
}

/* Component.module.css */
.component {
  background-color: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
}

Complex Animations

CSS Modules excel at complex animations:

/* Modal.module.css */
.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 200ms ease;
}

.overlayVisible {
  composes: overlay;
  opacity: 1;
}

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.95);
  opacity: 0;
  transition: all 200ms cubic-bezier(0.16, 1, 0.3, 1);
}

.modalVisible {
  composes: modal;
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
}

@keyframes slideIn {
  from {
    transform: translate(-50%, -50%) translateY(20px) scale(0.95);
    opacity: 0;
  }
  to {
    transform: translate(-50%, -50%) translateY(0) scale(1);
    opacity: 1;
  }
}

.modalAnimated {
  animation: slideIn 300ms cubic-bezier(0.16, 1, 0.3, 1);
}

Hybrid Approach: CSS Modules + Tailwind

Use both together for maximum flexibility:

import styles from './ComplexCard.module.css'

function ComplexCard({ title, children }: Props) {
  return (
    // Tailwind for layout, CSS Module for complex styles
    <div className={`${styles.card} p-4 md:p-6`}>
      <h2 className={`${styles.title} text-lg font-semibold mb-2`}>
        {title}
      </h2>
      <div className={styles.animatedContent}>
        {children}
      </div>
    </div>
  )
}

When to Use Which

Scenario Approach
Layout utilities (flex, grid, spacing) Tailwind
Responsive utilities Tailwind
State variants (hover, focus) Tailwind
Complex animations CSS Modules
Keyframe animations CSS Modules
Third-party component overrides CSS Modules
Component state classes CSS Modules
Design system utilities Tailwind
One-off complex styles CSS Modules

Performance Benefits

Lightning CSS Advantages

Feature Speed Improvement
CSS parsing 100x faster than PostCSS
Vendor prefixing Built-in, instant
Minification Faster than cssnano
Bundling Parallel processing

Dead Code Elimination

Vite automatically eliminates unused CSS:

  • CSS Modules are naturally tree-shaken (only imported classes included)
  • Lightning CSS removes unused selectors

Bundle Size Optimization

// vite.config.ts
export default defineConfig({
  build: {
    cssMinify: 'lightningcss',
    cssCodeSplit: true,  // Separate CSS per chunk
  }
})

Related Skills

  • tailwindcss - Utility-first CSS (complementary approach)
  • shadcn-ui - Component library using CSS variables
  • react-typescript - Component patterns with className
  • testing-frontend - Testing styled components