Files
gh-otoshek-claude-code-toolkit/skills/tailwind-setup/SKILL.md
2025-11-30 08:46:40 +08:00

9.8 KiB

name, description, allowed-tools
name description allowed-tools
tailwind-setup Configure Tailwind CSS and shadcn/ui for React frontends with Django backends, including dark mode support and theme tokens. This skill should be used when setting up a new React project or adding Tailwind to an existing one. Bash, Write, Edit, Read, Glob, Grep, TodoWrite, mcp__shadcn__get_project_registries, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_item_examples_from_registries, mcp__shadcn__get_add_command_for_items, mcp__shadcn__get_audit_checklist

Purpose

Configure Tailwind CSS with shadcn/ui for React frontends connecting to Django backends. This setup includes dark mode support, theme tokens, and shadcn/ui component integration. The configuration enables automatic dark mode switching and provides a foundation for building consistent, themeable user interfaces.

Setup Process Overview

Follow these steps in order:

  1. Install Dependencies
  2. Configure Vite for Django Backend
  3. Setup Tailwind in CSS
  4. Apply Base Theme (Required for dark mode support)
  5. Setup shadcn/ui
  6. Implement Dark Mode with Toggle
  7. Verify Setup and Check for Errors

Step 1: Install Dependencies

Install Tailwind CSS and the Vite plugin:

npm --prefix ./frontend tailwindcss @tailwindcss/vite

Step 2: Configure Vite for Django Backend

Configure vite.config.js to enable Tailwind and path aliases:

  1. Read existing vite.config.js
  2. Add the required imports at the top (keep the existing React import):
    import path from 'node:path'
    import tailwindcss from '@tailwindcss/vite'
    
  3. Update the plugins array so Tailwind runs alongside React:
    plugins: [
      react(),
      tailwindcss(),
    ]
    
  4. Define the alias block in the same file so @/ resolves to src (this relies on the path import above):
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
      },
    }
    

Step 3: Setup Tailwind in CSS

Configure the main CSS file to import Tailwind:

  1. Read existing src/index.css
  2. Replace the entire contents with the Tailwind import:
    @import "tailwindcss";
    

Step 4: Apply Base Theme

Apply the base theme by running the bundled script from the project root directory at: scripts/apply-theme.sh

Apply the base theme by running the script from the skill's bundled resources at:scripts/apply-theme.sh within this skill's directory.

The script requires requires the frontend directory path (relative to project root) as an argument.

  1. Run the theme script from project root:
    bash <path-to-skill>/scripts/apply-theme.sh frontend
    

The script appends shadcn/ui's design tokens (from assets/tailwind-theme.css, in the skill's bundled resources) to src/index.css, adding support for typography, colors, and automatic dark mode.

Step 5: Setup shadcn/ui

5a. Install shadcn/ui Dependencies

Install the required packages for shadcn/ui component functionality:

npm --prefix ./frontend install class-variance-authority clsx tailwind-merge lucide-react

5b. Create jsconfig.json for Path Aliases

Create jsconfig.json in the frontend root directory:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

5c. Create Utility Helper Function

Create src/lib/utils.js:

import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
  return twMerge(clsx(inputs))
}

5d. Create components.json Configuration

Create components.json in the frontend root directory:

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": false,
  "tsx": false,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/index.css",
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

5e. Install shadcn/ui Components

PREREQUISITE: Before installing components, verify their availability and review usage patterns using the shadcn MCP server. This ensures compatibility with the current registry and helps understand proper implementation patterns.

MCP Verification:

Use the shadcn MCP tools to search for each component and confirm it exists in the registry:

  • mcp__shadcn__search_items_in_registries with registries=["@shadcn"] and query="card"
  • mcp__shadcn__search_items_in_registries with registries=["@shadcn"] and query="button"
  • mcp__shadcn__search_items_in_registries with registries=["@shadcn"] and query="navigation-menu"
  • mcp__shadcn__search_items_in_registries with registries=["@shadcn"] and query="alert"

Install Required Components:

Once verified, install the components using the shadcn CLI:

npx --prefix ./frontend shadcn@latest add card
npx --prefix ./frontend shadcn@latest add field
npx --prefix ./frontend shadcn@latest add input
npx --prefix ./frontend shadcn@latest add button
npx --prefix ./frontend shadcn@latest add table
npx --prefix ./frontend shadcn@latest add navigation-menu
npx --prefix ./frontend shadcn@latest add label
npx --prefix ./frontend shadcn@latest add separator
npx --prefix ./frontend shadcn@latest add alert

Step 6: Implement Dark Mode with Toggle

6a. Configure Tailwind for Class-based Dark Mode

Create or update tailwind.config.js in the frontend root directory, to enable class-based dark mode:

/** @type {import('tailwindcss').Config} */
export default {
  darkMode: 'class',
  content: [
    "./index.html",
    "./src/**/*.{js,jsx}",
  ],
}

6b. Create Theme Context

Create src/contexts/ThemeContext.jsx:

import { createContext, useContext, useEffect, useState } from "react";

const ThemeContext = createContext({
  theme: "system",
  setTheme: () => null,
});

export function ThemeProvider({ children, defaultTheme = "system", storageKey = "theme" }) {
  const [theme, setTheme] = useState(() => {
    return localStorage.getItem(storageKey) || defaultTheme;
  });

  useEffect(() => {
    const root = document.documentElement;

    root.classList.remove("dark");

    if (theme === "system") {
      const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
      if (systemTheme) {
        root.classList.add("dark");
      }
      return;
    }

    if (theme === "dark") {
      root.classList.add("dark");
    }
  }, [theme]);

  const value = {
    theme,
    setTheme: (newTheme) => {
      localStorage.setItem(storageKey, newTheme);
      setTheme(newTheme);
    },
  };

  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}

export function useTheme() {
  const context = useContext(ThemeContext);

  if (context === undefined) {
    throw new Error("useTheme must be used within a ThemeProvider");
  }

  return context;
}

6c. Create Theme Toggle Button Component

Create src/components/ThemeToggle.jsx:

import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/contexts/ThemeContext";

export default function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  const toggleTheme = () => {
    // If currently system or light, switch to dark
    // If currently dark, switch to light
    if (theme === "dark") {
      setTheme("light");
    } else {
      setTheme("dark");
    }
  };

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={toggleTheme}
      aria-label="Toggle theme"
      className="cursor-pointer"
    >
      <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
    </Button>
  );
}

6d. Wrap App with ThemeProvider

Update src/App.jsx to include the ThemeProvider:

import Router from "./router";
import { ThemeProvider } from "@/contexts/ThemeContext";

function App() {
  return (
    <ThemeProvider defaultTheme="system" storageKey="theme">
      <Router />
    </ThemeProvider>
  );
}

export default App;

6e. Add Theme Toggle to Navbar

Integrate the ThemeToggle component into the navigation bar:

  1. Locate the navbar component: Find the main navigation component (commonly in src/components/Navbar.jsx or similar)
  2. Import the ThemeToggle component:
    import ThemeToggle from "@/components/ThemeToggle";
    
  3. Add the toggle to the navbar: Place the <ThemeToggle /> component in an appropriate location within the navbar, typically in the right section alongside other navigation items:
    <nav className="flex items-center justify-between p-4">
      <div>{/* Logo and left-side nav items */}</div>
      <div className="flex items-center gap-4">
        {/* Other right-side nav items */}
        <ThemeToggle />
      </div>
    </nav>
    

Step 7: Run Build to Check for Compilation Errors

Build the project to verify there are no compilation errors:

npm --prefix ./frontend run build

If the build succeeds, the setup is complete. If there are errors:

  • Check for missing imports or incorrect file paths
  • Verify all components were installed correctly
  • Ensure jsconfig.json path aliases match vite.config.js aliases
  • Confirm tailwind.config.js content paths include all component files

Setup Complete

The Tailwind CSS and shadcn/ui setup is now complete. The project includes:

  • Tailwind CSS with Vite integration
  • shadcn/ui design tokens and theme variables
  • Dark mode support with class-based switching
  • Path aliases configured for clean imports (@/components, @/lib, etc.)
  • Core shadcn/ui components installed and ready to use