AgentSkillsCN

Shadcn Ui Setup

Shadcn UI搭建

SKILL.md

shadcn/ui Setup

Skill Purpose: Component library initialization and customization patterns


Core Skill Pattern

Objective: Establish consistent component library setup with customizable components and design system integration.

Universal Pattern:

  1. Define component library requirements and scope
  2. Initialize component library and base components
  3. Configure customization and theming patterns
  4. Set up component organization and documentation
  5. Create component usage and contribution guidelines

Key Decisions (Project-Specific):

  • Component library selection and scope
  • Customization level vs out-of-the-box usage
  • Design system integration approach
  • Component organization and naming conventions
  • Team contribution and maintenance processes

Project-Specific Implementation Notes

Customize per project:

  • Component selection based on UI requirements
  • Customization level based on design needs
  • Integration approach based on existing design system
  • Organization based on project complexity and team size
  • Documentation depth based on team experience

Example Implementation (shadcn/ui with Next.js Pattern)

Note: This is an example pattern using shadcn/ui. Adapt component library and configuration based on your specific project requirements and design needs.

Prerequisites (Example)

  • Project initialized
  • CSS framework configured
  • Component library selected

Example: shadcn/ui Component Library Implementation

Framework-Specific Example: This demonstrates the pattern using shadcn/ui with Next.js. Adapt for your component library and framework.

1. Install shadcn/ui CLI

bash
# Install shadcn/ui CLI globally
npm install -g shadcn-ui@latest

# Or use npx to run without global installation
npx shadcn-ui@latest init

2. Initialize shadcn/ui

bash
# Initialize shadcn/ui in your project
npx shadcn-ui@latest init

When prompted, use these settings:

  • Would you like to use TypeScript? → Yes
  • Which style would you like to use? → New York
  • Which color would you like to use as base color? → Slate
  • Where is your global CSS file? → src/app/globals.css
  • Would you like to use CSS variables for colors? → Yes
  • Where is your tailwind.config.js located? → tailwind.config.ts
  • Configure the import alias for components? → src/components
  • Configure the import alias for utils? → src/lib/utils

3. Update components.json

Update components.json:

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

4. Install Core Dependencies

bash
# Install required dependencies
npm install class-variance-authority clsx tailwind-merge lucide-react

# Install additional dependencies for components
npm install @radix-ui/react-slot @radix-ui/react-separator @radix-ui/react-label

5. Update Tailwind Configuration

Update tailwind.config.ts to include shadcn/ui paths:

typescript
import type { Config } from 'tailwindcss';

const config: Config = {
  darkMode: ["class"],
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  prefix: "",
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      keyframes: {
        "accordion-down": {
          from: { height: "0" },
          to: { height: "var(--radix-accordion-content-height)" },
        },
        "accordion-up": {
          from: { height: "var(--radix-accordion-content-height)" },
          to: { height: "0" },
        },
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
} satisfies Config;

export default config;

6. Update Global CSS

Update src/app/globals.css with shadcn/ui styles:

css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 240 10% 3.9%;
    --card: 0 0% 100%;
    --card-foreground: 240 10% 3.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 240 10% 3.9%;
    --primary: 240 5.9% 10%;
    --primary-foreground: 0 0% 98%;
    --secondary: 240 4.8% 95.9%;
    --secondary-foreground: 240 5.9% 10%;
    --muted: 240 4.8% 95.9%;
    --muted-foreground: 240 3.8% 46.1%;
    --accent: 240 4.8% 95.9%;
    --accent-foreground: 240 5.9% 10%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 0 0% 98%;
    --border: 240 5.9% 90%;
    --input: 240 5.9% 90%;
    --ring: 240 5.9% 10%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 240 10% 3.9%;
    --foreground: 0 0% 98%;
    --card: 240 10% 3.9%;
    --card-foreground: 0 0% 98%;
    --popover: 240 10% 3.9%;
    --popover-foreground: 0 0% 98%;
    --primary: 0 0% 98%;
    --primary-foreground: 240 5.9% 10%;
    --secondary: 240 3.7% 15.9%;
    --secondary-foreground: 0 0% 98%;
    --muted: 240 3.7% 15.9%;
    --muted-foreground: 240 5% 64.9%;
    --accent: 240 3.7% 15.9%;
    --accent-foreground: 0 0% 98%;
    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 0 0% 98%;
    --border: 240 3.7% 15.9%;
    --input: 240 3.7% 15.9%;
    --ring: 240 4.9% 83.9%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}

7. Create Core Utilities

Update src/lib/utils.ts:

typescript
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

8. Install Essential Components

bash
# Install core UI components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add input
npx shadcn-ui@latest add label
npx shadcn-ui@latest add textarea
npx shadcn-ui@latest add select
npx shadcn-ui@latest add checkbox
npx shadcn-ui@latest add radio-group
npx shadcn-ui@latest add switch
npx shadcn-ui@latest add slider
npx shadcn-ui@latest add progress
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add toast
npx shadcn-ui@latest add table
npx shadcn-ui@latest add badge
npx shadcn-ui@latest add alert
npx shadcn-ui@latest add separator
npx shadcn-ui@latest add skeleton
npx shadcn-ui@latest add tabs
npx shadcn-ui@latest add accordion
npx shadcn-ui@latest add collapsible
npx shadcn-ui@latest add command
npx shadcn-ui@latest add context-menu
npx shadcn-ui@latest add hover-card
npx shadcn-ui@latest add menubar
npx shadcn-ui@latest add navigation-menu
npx shadcn-ui@latest add popover
npx shadcn-ui@latest add scroll-area
npx shadcn-ui@latest add sheet
npx shadcn-ui@latest add tooltip

9. Create Component Index

Create src/components/ui/index.ts:

typescript
// Re-export all UI components for easy importing
export * from './button';
export * from './card';
export * from './input';
export * from './label';
export * from './textarea';
export * from './select';
export * from './checkbox';
export * from './radio-group';
export * from './switch';
export * from './slider';
export * from './progress';
export * from './dialog';
export * from './dropdown-menu';
export * from './toast';
export * from './table';
export * from './badge';
export * from './alert';
export * from './separator';
export * from './skeleton';
export * from './tabs';
export * from './accordion';
export * from './collapsible';
export * from './command';
export * from './context-menu';
export * from './hover-card';
export * from './menubar';
export * from './navigation-menu';
export * from './popover';
export * from './scroll-area';
export * from './sheet';
export * from './tooltip';

10. Create Custom Components

Create src/components/ui/extended-button.tsx:

typescript
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';

import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive:
          'bg-destructive text-destructive-foreground hover:bg-destructive/90',
        outline:
          'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
        secondary:
          'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
        link: 'text-primary underline-offset-4 hover:underline',
        zeus: 'bg-zeus-500 text-white hover:bg-zeus-600',
        zeusOutline: 'border border-zeus-500 text-zeus-500 hover:bg-zeus-50',
      },
      size: {
        default: 'h-10 px-4 py-2',
        sm: 'h-9 rounded-md px-3',
        lg: 'h-11 rounded-md px-8',
        icon: 'h-10 w-10',
        xl: 'h-12 rounded-lg px-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

export interface ExtendedButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
  loading?: boolean;
}

const ExtendedButton = React.forwardRef<HTMLButtonElement, ExtendedButtonProps>(
  ({ className, variant, size, asChild = false, loading, children, disabled, ...props }, ref) => {
    const Comp = asChild ? Slot : 'button';
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        disabled={disabled || loading}
        {...props}
      >
        {loading ? (
          <div className="flex items-center gap-2">
            <div className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
            {children}
          </div>
        ) : (
          children
        )}
      </Comp>
    );
  }
);
ExtendedButton.displayName = 'ExtendedButton';

export { ExtendedButton, buttonVariants };

11. Create Form Components

Create src/components/ui/form.tsx:

typescript
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import {
  Controller,
  ControllerProps,
  FieldPath,
  FieldValues,
  useFormContext,
} from 'react-hook-form';

import { cn } from '@/lib/utils';
import { Label } from '@/components/ui/label';

const Form = React.forwardRef<
  HTMLFormElement,
  React.FormHTMLAttributes<HTMLFormElement>
>(({ className, ...props }, ref) => {
  return (
    <form
      ref={ref}
      className={cn('space-y-6', className)}
      {...props}
    />
  );
});
Form.displayName = 'Form';

interface FormFieldContextValue<TFieldValues extends FieldValues> {
  name: FieldPath<TFieldValues>;
}

const FormFieldContext = React.createContext<FormFieldContextValue<any>>(
  {} as any
);

const FormField = <
  TFieldValues extends FieldValues = FieldValues
>({
  ...props
}: ControllerProps<TFieldValues>) => {
  return (
    <FormFieldContext.Provider value={{ name: props.name }}>
      <Controller {...props} />
    </FormFieldContext.Provider>
  );
};

const useFormField = () => {
  const fieldContext = React.useContext(FormFieldContext);
  const itemContext = React.useContext(FormItemContext);
  const { getFieldState, formState } = useFormContext();

  const fieldState = getFieldState(fieldContext.name, formState);

  if (!fieldContext) {
    throw new Error('useFormField should be used within <FormField>');
  }

  const { id } = itemContext;

  return {
    id,
    name: fieldContext.name,
    formItemId: `${id}-form-item`,
    formDescriptionId: `${id}-form-item-description`,
    formMessageId: `${id}-form-item-message`,
    ...fieldState,
  };
};

interface FormItemContextValue {
  id: string;
}

const FormItemContext = React.createContext<FormItemContextValue>(
  {} as any
);

const FormItem = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const id = React.useId();

  return (
    <FormItemContext.Provider value={{ id }}>
      <div ref={ref} className={cn('space-y-2', className)} {...props} />
    </FormItemContext.Provider>
  );
});
FormItem.displayName = 'FormItem';

const FormLabel = React.forwardRef<
  React.ElementRef<typeof Label>,
  React.ComponentPropsWithoutRef<typeof Label>
>(({ className, ...props }, ref) => {
  const { error, formItemId } = useFormField();

  return (
    <Label
      ref={ref}
      className={cn(error && 'text-destructive', className)}
      htmlFor={formItemId}
      {...props}
    />
  );
});
FormLabel.displayName = 'FormLabel';

const FormControl = React.forwardRef<
  React.ElementRef<typeof Slot>,
  React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
  const { error, formItemId, formDescriptionId, formMessageId } = useFormField();

  return (
    <Slot
      ref={ref}
      id={formItemId}
      aria-describedby={
        !error
          ? `${formDescriptionId}`
          : `${formDescriptionId} ${formMessageId}`
      }
      aria-invalid={!!error}
      {...props}
    />
  );
});
FormControl.displayName = 'FormControl';

const FormDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
  const { formDescriptionId } = useFormField();

  return (
    <p
      ref={ref}
      id={formDescriptionId}
      className={cn('text-sm text-muted-foreground', className)}
      {...props}
    />
  );
});
FormDescription.displayName = 'FormDescription';

const FormMessage = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
  const { error, formMessageId } = useFormField();
  const body = error ? String(error?.message) : children;

  if (!body) {
    return null;
  }

  return (
    <p
      ref={ref}
      id={formMessageId}
      className={cn('text-sm font-medium text-destructive', className)}
      {...props}
    >
      {body}
    </p>
  );
});
FormMessage.displayName = 'FormMessage';

export {
  useFormField,
  Form,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
  FormField,
};

12. Create Theme Provider

Create src/components/theme-provider.tsx:

typescript
'use client';

import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import { type ThemeProviderProps } from 'next-themes/dist/types';

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

13. Update Package.json Scripts

Update package.json scripts:

json
{
  "scripts": {
    "shadcn:add": "shadcn-ui@latest add",
    "shadcn:update": "shadcn-ui@latest update",
    "shadcn:diff": "shadcn-ui@latest diff"
  }
}

Code Examples

Using shadcn/ui Components

typescript
// Basic button usage
import { Button } from '@/components/ui/button';

export function ExampleButton() {
  return (
    <div className="flex gap-4">
      <Button>Default Button</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
    </div>
  );
}

// Card component usage
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';

export function ExampleCard() {
  return (
    <Card className="w-[350px]">
      <CardHeader>
        <CardTitle>Card Title</CardTitle>
        <CardDescription>Card Description</CardDescription>
      </CardHeader>
      <CardContent>
        <p>Card content goes here.</p>
      </CardContent>
      <CardFooter className="flex justify-between">
        <Button variant="outline">Cancel</Button>
        <Button>Save</Button>
      </CardFooter>
    </Card>
  );
}

// Form with shadcn/ui
import { useForm } from 'react-hook-form';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';

export function ExampleForm() {
  const form = useForm();

  return (
    <Form {...form}>
      <FormField
        control={form.control}
        name="username"
        render={({ field }) => (
          <FormItem>
            <FormLabel>Username</FormLabel>
            <FormControl>
              <Input placeholder="Enter your username" {...field} />
            </FormControl>
            <FormDescription>
              This is your public display name.
            </FormDescription>
            <FormMessage />
          </FormItem>
        )}
      />
      <Button type="submit">Submit</Button>
    </Form>
  );
}

Custom Component Extensions

typescript
// Extended button with loading state
import { ExtendedButton } from '@/components/ui/extended-button';

export function LoadingButtonExample() {
  return (
    <div className="flex gap-4">
      <ExtendedButton loading>Loading...</ExtendedButton>
      <ExtendedButton variant="zeus">Zeus Style</ExtendedButton>
      <ExtendedButton size="xl">Extra Large</ExtendedButton>
    </div>
  );
}

// Custom card with hover effects
import { Card, CardContent } from '@/components/ui/card';
import { cn } from '@/lib/utils';

interface CustomCardProps extends React.HTMLAttributes<HTMLDivElement> {
  hover?: boolean;
}

export function CustomCard({ className, hover, ...props }: CustomCardProps) {
  return (
    <Card
      className={cn(
        'transition-all duration-200',
        hover && 'hover:shadow-lg hover:scale-105',
        className
      )}
      {...props}
    />
  );
}

Theme Integration

typescript
// Theme-aware component
'use client';

import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
import { Moon, Sun } from 'lucide-react';

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

  return (
    <Button
      variant="outline"
      size="icon"
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
    >
      <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
    </Button>
  );
}

Configuration Templates

Complete components.json for Zeus Framework

json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "src/app/globals.css",
    "baseColor": "slate",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks",
    "types": "@/types"
  }
}

Best Practices

  1. Use the CLI for component management - Ensures proper installation
  2. Customize components thoughtfully - Maintain consistency
  3. Use form components with react-hook-form - Type-safe forms
  4. Implement dark mode properly - Use theme provider
  5. Create reusable variants - Use class-variance-authority
  6. Document custom components - Team collaboration
  7. Keep components minimal - Single responsibility principle
  8. Use proper TypeScript types - Type safety

Stop Conditions

STOP and report if:

  • shadcn/ui CLI fails to initialize
  • Component installation fails
  • Tailwind CSS conflicts occur
  • TypeScript errors in components
  • Theme switching doesn't work

Expected Outcomes:

  • All shadcn/ui components install correctly
  • Components render without errors
  • Theme switching works properly
  • TypeScript types are correct
  • Custom components extend base components

Verification Checklist

  • shadcn/ui CLI initializes successfully
  • All essential components install
  • Components render correctly
  • Dark mode functions properly
  • Form components work with react-hook-form
  • Custom components extend properly
  • TypeScript types are correct
  • Theme provider works
  • Component variants function
  • No CSS conflicts exist

Version: 1.0.0 Last Updated: 2026-01-31 Skill Category: Architecture - Scaffolding