Customize UI Components
Fine-tune button styles, cards, inputs, and other UI components to match your design
The SaaS starter kit includes a set of reusable UI components (Button, Card, Input, Badge, etc.) that you can customize to match your design system. All components use Tailwind CSS and automatically adapt to light/dark mode.
Prerequisites
- Basic understanding of Tailwind CSS classes
- Familiarity with React and TypeScript
- Completed Theme Colors guide (optional but recommended)
Component Library Overview
All UI components are located in src/components/ui/ and follow consistent patterns:
Button Component
Variants: primary, secondary, danger, ghost
Sizes: sm, md, lg
States: default, hover, disabled, loading
Card Component
Parts: Card, CardHeader, CardBody, CardFooter
Auto-adapts to dark mode
Customizable borders and padding
Customizing Components
Understand Component Structure
Each component is built with Tailwind CSS classes and uses the CSS variable system. Here's the Button component structure:
1type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";2 3const variantStyles: Record<ButtonVariant, string> = {4 primary: "bg-primary-900 text-white hover:bg-primary-800 ...",5 secondary: "border border-primary-200 bg-white ...",6 danger: "border border-danger-200 bg-white text-danger-600 ...",7 ghost: "text-primary-600 hover:bg-primary-100 ...",8};CSS Variables
primary-900 and danger-600 which you customized in the Theme Colors guide. Changing those variables automatically updates all components.Modify Button Styles
To change button appearance, edit the variant styles in src/components/ui/Button.tsx:
Example: Make primary buttons more rounded
1export const Button = forwardRef<HTMLButtonElement, ButtonProps>(2 ({ variant = "primary", ... }) => {3 const baseClassName = `4 inline-flex items-center justify-center5 font-medium transition-colors6 rounded-md // Change to: rounded-full for pill-shaped buttons7 ${variantStyles[variant]}8 ${sizeStyles[size]}9 `;10 }11);Example: Adjust button sizes
1const sizeStyles: Record<ButtonSize, string> = {2 sm: "h-8 px-3 text-xs", // Small: 32px height3 md: "h-10 px-4 text-sm", // Medium: 40px height (default)4 lg: "h-12 px-6 text-base", // Large: 48px height5};6 7// To make buttons bigger overall, increase h- and px- values:8const sizeStyles: Record<ButtonSize, string> = {9 sm: "h-9 px-4 text-sm", // Slightly larger small10 md: "h-11 px-5 text-base", // Larger medium11 lg: "h-14 px-8 text-lg", // Larger large12};Example: Create a new button variant
1type ButtonVariant = "primary" | "secondary" | "danger" | "ghost" | "success";2 3const variantStyles: Record<ButtonVariant, string> = {4 // ... existing variants5 success:6 "bg-success-600 text-white hover:bg-success-700 " +7 "dark:bg-success-500 dark:hover:bg-success-600",8};9 10// Usage:11<Button variant="success">Save Changes</Button>Customize Card Components
Cards are simple wrappers with consistent styling. Modify them in src/components/ui/Card.tsx:
Example: Add shadow to cards
1export function Card({ children, className = "" }: CardProps) {2 return (3 <div className={`4 rounded-lg border border-primary-200 bg-white5 shadow-md // Add shadow here6 dark:border-primary-800 dark:bg-primary-9507 ${className}8 `}>9 {children}10 </div>11 );12}Example: Adjust card padding
1export function CardBody({ children, className = "" }: CardProps) {2 return (3 <div className={`px-6 py-4 ${className}`}> // Default: 24px x 16px4 {children}5 </div>6 );7}8 9// To increase padding:10<div className={`px-8 py-6 ${className}`}> // 32px x 24pxExample: Make cards more rounded
1export function Card({ children, className = "" }: CardProps) {2 return (3 <div className={`4 rounded-lg // Default: 8px5 // Change to:6 rounded-xl // 12px7 // Or:8 rounded-2xl // 16px9 `}>10 );11}Customize Input Fields
Input components can be styled for better visual consistency:
1export function Input({ className = "", ...props }: InputProps) {2 return (3 <input4 className={`5 h-10 w-full rounded-md border px-3 py-26 border-primary-200 bg-white7 text-sm text-primary-9508 placeholder:text-primary-4009 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/2010 disabled:cursor-not-allowed disabled:opacity-5011 dark:border-primary-800 dark:bg-primary-95012 dark:text-primary-50 dark:placeholder:text-primary-50013 ${className}14 `}15 {...props}16 />17 );18}Customization Tips
- Adjust
h-10to change input height - Modify
rounded-mdfor different corner radius - Change
focus:ring-primary-500/20for custom focus effects
Customize Badge Component
Badges are used for status indicators and labels. Customize their appearance:
1type BadgeVariant = "default" | "primary" | "success" | "danger";2 3const variantStyles: Record<BadgeVariant, string> = {4 default: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100",5 primary: "bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-100",6 success: "bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-100",7 danger: "bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-100",8};9 10export function Badge({ variant = "default", children }: BadgeProps) {11 return (12 <span className="...">13 {children}14 </span>15 );16}Customize Animation Timing
Adjust animation speed and easing across all components:
1:root {2 /* Animation Durations */3 --duration-fast: 150ms; /* Button hovers, small transitions */4 --duration-normal: 250ms; /* Standard transitions */5 --duration-slow: 350ms; /* Page transitions, modals */6 --duration-slower: 500ms; /* Complex animations */7 8 /* Animation Easings */9 --ease-out: cubic-bezier(0.16, 1, 0.3, 1);10 --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);11 --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);12 --ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);13}Animation Guide
Advanced Customization Techniques
Using className Prop
All components accept a className prop to override styles on a per-use basis:
1// Default button2<Button>Click me</Button>3 4// Custom styling for this specific button5<Button className="bg-gradient-to-r from-purple-600 to-pink-600">6 Gradient Button7</Button>8 9// Custom card with extra spacing10<Card className="p-8 shadow-xl">11 <CardBody>Premium content</CardBody>12</Card>Creating Component Variants
Extend components with new variants for specific use cases:
1// Add an "outline" variant2type ButtonVariant = "primary" | "secondary" | "danger" | "ghost" | "outline";3 4const variantStyles: Record<ButtonVariant, string> = {5 // ... existing variants6 outline:7 "border-2 border-primary-600 bg-transparent text-primary-600 " +8 "hover:bg-primary-600 hover:text-white " +9 "dark:border-primary-400 dark:text-primary-400 " +10 "dark:hover:bg-primary-400 dark:hover:text-primary-950",11};12 13// Usage:14<Button variant="outline">Outlined Button</Button>Consistent Spacing System
Use Tailwind's spacing scale consistently across components:
1// Tailwind spacing scale:2// 1 unit = 0.25rem (4px)3 4p-2 â 8px padding5p-4 â 16px padding6p-6 â 24px padding7p-8 â 32px padding8 9gap-3 â 12px gap between flex/grid items10gap-4 â 16px gap11gap-6 â 24px gap12 13// Recommendation: stick to 4, 6, 8 for consistencyResponsive Design
Add responsive breakpoints to adapt components for different screen sizes:
1<Button className="2 w-full // Full width on mobile3 md:w-auto // Auto width on tablet and up4 text-sm // Small text on mobile5 md:text-base // Regular text on tablet and up6">7 Responsive Button8</Button>9 10<Card className="11 p-4 // 16px padding on mobile12 md:p-6 // 24px padding on tablet13 lg:p-8 // 32px padding on desktop14">15 {children}16</Card>Common Customization Examples
Example 1: Softer, Rounded Design
Make the entire app feel softer with more rounded corners:
1// Button.tsx: rounded-md â rounded-full2// Card.tsx: rounded-lg â rounded-2xl3// Input.tsx: rounded-md â rounded-lg4// Badge.tsx: rounded-full (already rounded)Example 2: Bold, High-Contrast Design
Increase visual weight and contrast:
1// Button.tsx: font-medium â font-bold2// Card.tsx: border-primary-200 â border-primary-3003// CardHeader: text-lg â text-xl4// Add shadow-lg to all CardsExample 3: Minimalist, Borderless Design
Remove borders and use subtle backgrounds:
1// Card.tsx: Remove border, use bg-gray-50/bg-gray-900 instead2// Button secondary: Remove border, use subtle bg3// Input: border â border-0, use bg-gray-100 insteadTroubleshooting
Styles not updating
If component style changes don't appear:
- Save the file and wait for dev server to reload
- Hard refresh browser (Cmd+Shift+R / Ctrl+Shift+R)
- Check for TypeScript errors in terminal
- Verify Tailwind classes are spelled correctly
Dark mode looks broken
Make sure to include both light and dark mode classes:
1// â Wrong: Only light mode2className="bg-white text-gray-900"3 4// â
Correct: Both modes5className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"Custom classes not working
If custom Tailwind classes don't work:
- Avoid dynamic class names:
bg-${color}-500won't work - Use complete class names:
bg-primary-500notbg-primary - Check Tailwind CSS is imported in
globals.css - Restart dev server if adding new utility classes
Components look inconsistent
To maintain consistency:
- Use the same color palette (primary, success, danger, warning)
- Stick to the spacing scale (4, 6, 8, 12, 16, 24px)
- Keep border radius consistent across all components
- Use the same font weights throughout (medium, semibold, bold)
Related Guides
Next Steps
Now that you've customized your UI components, you might want to:
- Customize Animations - Learn how to adjust and customize component animations
- Review Theme Colors - Ensure your color palette works with component changes
- Customize Branding - Update site name and logo to complete the look
- Production Deployment - Deploy your customized app