Customization

Customize UI Lab's design system to match your brand and requirements using Tailwind CSS design tokens.

Design tokens

UI Lab uses Tailwind CSS design tokens (CSS variables) to control all visual aspects. Modify tokens in your globals.css file to customize the entire design system:

/* globals.css */
@theme {
  --color-base-50: #ffffff;
  --color-base-100: #f8f8f8;
  --color-base-200: #efefef;
  /* ... continues to 950 */

  --spacing: 0.25rem;
  --radius-sm: 0.375rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-full: 9999px;

  --font-sans: ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'Fira Code', monospace;
}

All components automatically use these tokens. Change a single token and it updates across your entire application.

Color system

UI Lab's base palette automatically inverts across light and dark modes. The palette runs from 50 (lightest) to 950 (darkest).

Understanding the base palette

In dark mode (default), lower values (50-400) are light colors used for text and overlays. Higher values (600-950) are dark colors for backgrounds.

In light mode, this inverts: lower values become dark, higher values become light. This semantic approach eliminates the need to manage separate color systems.

/* Dark mode (default) */
--color-base-50: #ffffff;    /* lightest, for text */
--color-base-100: #f8f8f8;
--color-base-200: #efefef;
--color-base-900: #1a1a1a;   /* darkest, for bg */
--color-base-950: #0f0f0f;

/* Light mode inverts these automatically */

Custom brand colors

Add accent colors for your brand. Keep the base palette but extend it with semantic colors:

@theme {
  /* Keep base palette as-is */
  --color-base-50: #ffffff;
  /* ... */

  /* Add accent colors */
  --color-accent-500: #0066ff;
  --color-accent-600: #0052cc;
  --color-accent-700: #003d99;

  /* Semantic colors */
  --color-success-500: #10b981;
  --color-warning-500: #f59e0b;
  --color-destructive-500: #ef4444;
}

Use these colors in components via Tailwind classes:

<Button className="bg-accent-500 hover:bg-accent-600">
  Branded Action
</Button>

Typography

Customize fonts, sizes, and weights through Tailwind configuration:

Font families

@theme {
  --font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'Fira Code', 'Courier New', monospace;
}

Font sizes

UI Lab uses a modular scale for sizes. Adjust in Tailwind config:

export default {
  theme: {
    extend: {
      fontSize: {
        xs: ['0.75rem', { lineHeight: '1rem' }],
        sm: ['0.875rem', { lineHeight: '1.25rem' }],
        base: ['1rem', { lineHeight: '1.5rem' }],
        lg: ['1.125rem', { lineHeight: '1.75rem' }],
        xl: ['1.25rem', { lineHeight: '1.75rem' }],
        '2xl': ['1.5rem', { lineHeight: '2rem' }],
        '3xl': ['1.875rem', { lineHeight: '2.25rem' }],
        '4xl': ['2.25rem', { lineHeight: '2.5rem' }],
      },
    },
  },
};

Font weights

Components use specific weights. Define available weights:

@theme {
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
}

Spacing and sizing

UI Lab uses a consistent spacing scale. The base unit is 0.25rem (4px):

@theme {
  --spacing: 0.25rem; /* 4px base unit */

  /* Components use multiples */
  /* spacing-1 = 0.25rem, spacing-2 = 0.5rem, etc */
}

/* Use in components */
<div className="p-4 gap-6 m-8">
  {/* p-4 = 1rem, gap-6 = 1.5rem, m-8 = 2rem */}
</div>

Border radius follows the same consistent scale:

@theme {
  --radius-sm: 0.375rem;    /* 6px */
  --radius-md: 0.5rem;      /* 8px */
  --radius-lg: 0.75rem;     /* 12px */
  --radius-full: 9999px;
}

Creating component variants

UI Lab components support the variant prop. Create custom variants by extending component source:

Example: Custom button variant

If you copied component source into src/components/ui/button.tsx, you can add variants:

// components/ui/button.tsx
const variants = cva('base-button-styles', {
  variants: {
    variant: {
      primary: 'bg-accent-500 text-background-950',
      secondary: 'bg-background-800 text-foreground-50',
      tertiary: 'bg-transparent text-accent-500',
      destructive: 'bg-destructive-500 text-white',
      outline: 'border border-accent-500 text-accent-500',
      // Add your custom variant
      gradient: 'bg-gradient-to-r from-accent-500 to-accent-600',
    },
  },
});

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof variants> {}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant = 'primary', ...props }, ref) => (
    <button ref={ref} className={variants({ variant })} {...props} />
  ),
);

Use your custom variant:

<Button variant="gradient">Gradient Button</Button>

Extending Tailwind configuration

Add project-specific utilities and styles through Tailwind's extend mechanism:

// tailwind.config.js
export default {
  theme: {
    extend: {
      colors: {
        brand: '#0066ff',
        'brand-light': '#e6f0ff',
      },
      spacing: {
        gutter: '1.5rem',
        'gutter-lg': '3rem',
      },
      keyframes: {
        slideDown: {
          '0%': { transform: 'translateY(-10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
      animation: {
        slideDown: 'slideDown 0.3s ease-out',
      },
    },
  },
};

Use these custom utilities in your components:

<div className="p-gutter bg-brand-light rounded animate-slideDown">
  Custom styled element
</div>

Theme switching

UI Lab's base palette automatically inverts in light mode. Implement theme switching at your app root:

// app/layout.tsx
'use client';

import { useState, useEffect } from 'react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const [theme, setTheme] = useState('dark');

  useEffect(() => {
    const saved = localStorage.getItem('theme') || 'dark';
    setTheme(saved);
    document.documentElement.setAttribute('data-theme', saved);
  }, []);

  const toggleTheme = () => {
    const newTheme = theme === 'dark' ? 'light' : 'dark';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme);
    document.documentElement.setAttribute('data-theme', newTheme);
  };

  return (
    <html data-theme={theme}>
      <body>
        <button onClick={toggleTheme}>
          Switch to {theme === 'dark' ? 'light' : 'dark'} mode
        </button>
        {children}
      </body>
    </html>
  );
}

In your CSS, use the data-theme attribute to adjust tokens:

/* globals.css */
html[data-theme="dark"] {
  @theme {
    --color-base-50: #ffffff;
    --color-base-950: #0f0f0f;
  }
}

html[data-theme="light"] {
  @theme {
    --color-base-50: #0f0f0f;
    --color-base-950: #ffffff;
  }
}

Customization best practices

Use design tokens, not hard-coded values

Define tokens once in globals.css or tailwind.config.js, then reference them everywhere. This makes global changes trivial.

Keep spacing and sizing consistent

Use multiples of the base spacing unit (0.25rem) to maintain visual rhythm. Don't use arbitrary values like p-[13px].

Extend, don't override

Use theme.extend in Tailwind config to add custom utilities. Overriding the base theme can break component consistency.

Avoid component className overrides for layout

Don't use className to override a component's core styles. Instead, wrap components or create variants. This preserves component semantics and accessibility.