Theming

Customize the appearance of all Melony components to match your brand.

Overview

Melony provides a comprehensive theme system that lets you customize colors, spacing, typography, border radius, and more across all built-in components. Pass a theme object to MelonyProvider to apply your custom styles.

Basic Usage

Create a theme object and pass it to MelonyProvider:

import { MelonyProvider, type MelonyTheme } from "melony";

const customTheme: MelonyTheme = {
  colors: {
    primary: "#3b82f6",
    secondary: "#6366f1",
    success: "#10b981",
    warning: "#f59e0b",
    danger: "#ef4444",
  },
};

function App() {
  return (
    <MelonyProvider theme={customTheme}>
      {/* Your components */}
    </MelonyProvider>
  );
}

Complete Theme Structure

Here's the full theme interface with all available options:

const theme: MelonyTheme = {
  colors: {
    primary: "#3b82f6",
    secondary: "#6366f1",
    success: "#10b981",
    warning: "#f59e0b",
    danger: "#ef4444",
    muted: "#6b7280",
    background: "#ffffff",
    foreground: "#000000",
    border: "#e5e7eb",
  },
  spacing: {
    xs: "4px",
    sm: "8px",
    md: "16px",
    lg: "24px",
    xl: "32px",
  },
  typography: {
    fontFamily: "Inter, system-ui, sans-serif",
    fontSize: {
      xs: "12px",
      sm: "14px",
      md: "16px",
      lg: "18px",
      xl: "24px",
    },
    fontWeight: {
      normal: "400",
      medium: "500",
      semibold: "600",
      bold: "700",
    },
  },
  borderRadius: {
    sm: "4px",
    md: "8px",
    lg: "12px",
    full: "9999px",
  },
  shadows: {
    sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
    md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
    lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
  },
};

Color System

Melony's color system includes semantic colors for different UI states:

colors: {
  // Brand colors
  primary: "#3b82f6",      // Primary actions, links
  secondary: "#6366f1",    // Secondary actions
  
  // Status colors
  success: "#10b981",      // Success states
  warning: "#f59e0b",      // Warning states
  danger: "#ef4444",       // Error, destructive actions
  
  // UI colors
  muted: "#6b7280",        // Muted text, disabled states
  background: "#ffffff",   // Background color
  foreground: "#000000",   // Text color
  border: "#e5e7eb",       // Border color
}

Components like <button> and <badge> automatically use these colors based on their variant prop.

Spacing Scale

Define a consistent spacing scale for gaps, padding, and margins:

spacing: {
  xs: "4px",    // Extra small
  sm: "8px",    // Small
  md: "16px",   // Medium (default)
  lg: "24px",   // Large
  xl: "32px",   // Extra large
}

Used by components like <row>, <column>, and <card> for consistent spacing.

Typography

Customize fonts, sizes, and weights:

typography: {
  fontFamily: "Inter, system-ui, sans-serif",
  fontSize: {
    xs: "12px",
    sm: "14px",
    md: "16px",
    lg: "18px",
    xl: "24px",
  },
  fontWeight: {
    normal: "400",
    medium: "500",
    semibold: "600",
    bold: "700",
  },
}

Dark Mode Example

Create a dark theme by adjusting colors:

const darkTheme: MelonyTheme = {
  colors: {
    primary: "#60a5fa",
    secondary: "#818cf8",
    success: "#34d399",
    warning: "#fbbf24",
    danger: "#f87171",
    muted: "#9ca3af",
    background: "#0f172a",
    foreground: "#f8fafc",
    border: "#334155",
  },
  // ... other theme properties
};

function App() {
  const [isDark, setIsDark] = useState(false);
  
  return (
    <MelonyProvider theme={isDark ? darkTheme : lightTheme}>
      <button onClick={() => setIsDark(!isDark)}>
        Toggle Theme
      </button>
      {/* Your components */}
    </MelonyProvider>
  );
}

Partial Themes

You don't need to define all theme properties. Melony uses defaults for any missing values:

// Only customize colors
const minimalTheme = {
  colors: {
    primary: "#8b5cf6",
    secondary: "#ec4899",
  },
};

// Only customize spacing
const spacingTheme = {
  spacing: {
    xs: "2px",
    sm: "4px",
    md: "8px",
    lg: "16px",
    xl: "24px",
  },
};

Dynamic Theming

Change themes dynamically based on user preferences:

import { useState, useEffect } from "react";

function App() {
  const [theme, setTheme] = useState(lightTheme);

  useEffect(() => {
    // Listen to system theme preference
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    
    const handleChange = (e: MediaQueryListEvent) => {
      setTheme(e.matches ? darkTheme : lightTheme);
    };

    mediaQuery.addEventListener("change", handleChange);
    setTheme(mediaQuery.matches ? darkTheme : lightTheme);

    return () => mediaQuery.removeEventListener("change", handleChange);
  }, []);

  return (
    <MelonyProvider theme={theme}>
      {/* Your components */}
    </MelonyProvider>
  );
}

Best Practices

  • Consistency: Use semantic color names consistently across your app
  • Accessibility: Ensure sufficient contrast ratios (WCAG AA: 4.5:1 minimum)
  • Spacing: Use a consistent spacing scale for visual harmony
  • Typography: Limit to 2-3 font sizes for clarity
  • Testing: Test your theme with all component variants