Skip to main content

Customization Guide

The T1 Component Library is designed to be highly customizable. This guide shows you how to customize styles, create custom variants, and extend the design system.

Customizing Component Styles

Using the className Prop

All components accept a className prop that merges with the default styles:
import { Button } from '@/components/Button';

<Button className="w-full rounded-full">
  Custom Button
</Button>
The className is applied last (Button.tsx:87), so it can override default styles:
className={`
  // ... default styles
  ${className}  // Your custom classes
`}

Overriding Default Styles

You can override any default style using Tailwind utilities:
// Override border radius
<Button className="rounded-none">
  Square Button
</Button>

// Override padding
<Card padding="md" className="p-8">
  Larger Padding
</Card>

// Override colors
<Button variant="primary" className="bg-purple-600 hover:bg-purple-700">
  Purple Button
</Button>

Creating Custom Variants

Extending Button Variants

You can create a wrapper component with custom variants:
import { Button, ButtonProps } from '@/components/Button';

type CustomVariant = 'success' | 'warning' | 'info';

interface CustomButtonProps extends Omit<ButtonProps, 'variant'> {
  variant?: ButtonProps['variant'] | CustomVariant;
}

const customVariantStyles: Record<CustomVariant, string> = {
  success: 'bg-success text-success-foreground hover:bg-success/90',
  warning: 'bg-warning text-warning-foreground hover:bg-warning/90',
  info: 'bg-blue-500 text-white hover:bg-blue-600',
};

export function CustomButton({ variant = 'primary', className = '', ...props }: CustomButtonProps) {
  // Check if it's a custom variant
  if (variant in customVariantStyles) {
    return (
      <Button 
        variant="primary" 
        className={`${customVariantStyles[variant as CustomVariant]} ${className}`}
        {...props}
      />
    );
  }
  
  // Use standard variant
  return <Button variant={variant as ButtonProps['variant']} className={className} {...props} />;
}
Usage:
<CustomButton variant="success">Success Action</CustomButton>
<CustomButton variant="warning">Warning Action</CustomButton>
<CustomButton variant="info">Info Action</CustomButton>

Extending Card Variants

Based on the pattern from Card.tsx:19, you can add new card styles:
import { Card } from '@/components/Card';

const customCardStyles = {
  gradient: 'bg-gradient-to-br from-primary to-accent text-white border-0',
  glass: 'bg-white/10 backdrop-blur-lg border border-white/20',
  neon: 'bg-card border-2 border-primary shadow-lg shadow-primary/50',
};

export function CustomCard({ style = 'default', children, ...props }) {
  const customClass = customCardStyles[style] || '';
  
  return (
    <Card className={customClass} {...props}>
      {children}
    </Card>
  );
}

Theme Customization

Modifying Design Tokens

Edit globals.css to customize the entire design system:
:root {
  /* Change primary color to purple */
  --primary: #9333ea;
  --primary-hover: #7e22ce;
  --primary-foreground: #ffffff;
  
  /* Customize border radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  
  /* Adjust transition speeds */
  --transition-fast: 100ms;
  --transition-normal: 250ms;
  --transition-slow: 400ms;
}
All components will automatically use the new values.

Creating Color Schemes

You can create multiple theme variants:
/* Ocean Theme */
[data-theme="ocean"] {
  --primary: #0ea5e9;
  --primary-hover: #0284c7;
  --accent: #06b6d4;
  --accent-hover: #0891b2;
}

/* Forest Theme */
[data-theme="forest"] {
  --primary: #22c55e;
  --primary-hover: #16a34a;
  --accent: #84cc16;
  --accent-hover: #65a30d;
}
Then apply them:
<div data-theme="ocean">
  <Button variant="primary">Ocean Button</Button>
</div>

Dark Mode Customization

Customize dark mode tokens in globals.css:71:
.dark {
  --background: #000000;  /* Darker background */
  --card: #111111;        /* Darker cards */
  --primary: #a78bfa;     /* Lighter primary for better contrast */
}

Component-Specific Customization

Custom Input Sizes

Extend the Input component with custom sizes:
import { Input } from '@/components/Input';

const customSizeStyles = {
  xs: 'px-2 py-1 text-xs',
  xl: 'px-6 py-4 text-xl',
};

export function CustomInput({ size, ...props }) {
  if (size === 'xs' || size === 'xl') {
    return <Input className={customSizeStyles[size]} {...props} />;
  }
  return <Input size={size} {...props} />;
}

Custom Card Compositions

Create reusable card patterns:
import { Card, CardHeader, CardBody, CardFooter, CardImage } from '@/components/Card';

export function ProductCard({ image, title, description, price, onBuy }) {
  return (
    <Card variant="elevated" hoverable className="max-w-sm">
      <CardImage src={image} alt={title} />
      <CardHeader>
        <h3 className="text-xl font-bold">{title}</h3>
      </CardHeader>
      <CardBody>
        <p className="text-muted-foreground">{description}</p>
        <p className="text-2xl font-bold text-primary mt-4">${price}</p>
      </CardBody>
      <CardFooter>
        <Button onClick={onBuy} className="w-full">
          Add to Cart
        </Button>
      </CardFooter>
    </Card>
  );
}

Utility Class Helpers

Create helper functions for common customizations:
// utils/styles.ts
export function cn(...classes: (string | undefined | null | false)[]) {
  return classes.filter(Boolean).join(' ');
}

export function getResponsiveSize(base: string, sm?: string, md?: string, lg?: string) {
  return cn(
    base,
    sm && `sm:${sm}`,
    md && `md:${md}`,
    lg && `lg:${lg}`
  );
}
Usage:
<Button 
  className={cn(
    'w-full',
    isLoading && 'opacity-50',
    isPrimary && 'ring-2 ring-primary'
  )}
>

Responsive Customization

Use Tailwind’s responsive prefixes:
<Button 
  size="sm" 
  className="sm:px-6 md:px-8 lg:px-10"
>
  Responsive Padding
</Button>

<Card 
  padding="sm" 
  className="md:p-6 lg:p-8"
>
  Responsive Card
</Card>

State-Based Customization

Customize styles based on component state:
function StatefulButton({ isActive, ...props }) {
  return (
    <Button 
      variant={isActive ? 'primary' : 'outline'}
      className={cn(
        'transition-all',
        isActive && 'scale-105 shadow-lg'
      )}
      {...props}
    />
  );
}

Advanced: Modifying Component Source

For deeper customization, you can modify the component source files:
  1. Copy the component to your project
  2. Modify the variant styles object
  3. Add new props or functionality
  4. Import from your custom location
Example - Adding a new Button variant:
// components/CustomButton.tsx
const variantStyles: Record<ButtonVariant, string> = {
  // ... existing variants
  gradient: `
    bg-gradient-to-r from-primary to-accent 
    text-white
    hover:scale-105
    shadow-lg hover:shadow-xl
  `,
};

Best Practices

  1. Use className for one-off customizations
  2. Create wrapper components for reusable patterns
  3. Modify design tokens for global changes
  4. Extend type definitions when adding new variants
  5. Keep customizations consistent with the design system
  6. Test responsive behavior across breakpoints
  7. Maintain accessibility when customizing interactive elements

Example: Complete Custom Theme

// theme/custom.css
:root {
  --primary: #8b5cf6;
  --primary-hover: #7c3aed;
  --accent: #ec4899;
  --accent-hover: #db2777;
  --radius-md: 1rem;
  --transition-normal: 300ms;
}

// components/CustomComponents.tsx
export function CustomButton(props) {
  return (
    <Button 
      className="font-bold tracking-wide uppercase"
      {...props}
    />
  );
}

export function CustomCard(props) {
  return (
    <Card 
      variant="elevated"
      className="border-2 border-primary/20"
      {...props}
    />
  );
}

Build docs developers (and LLMs) love