Extending Theme Types

Extending Theme Types

The theme system allows you to add custom theme tokens while keeping TypeScript type checking and autocomplete.

You can extend the following theme groups:

  • Colors
  • Disabled
  • Rounded
  • Shadow
  • Typography variants
  • Typography colors

There are two steps when adding a custom token:

  1. Extend the appropriate theme interface.
  2. Add the custom token to your createTheme configuration.

Where to Define Extensions

Theme extensions should be declared in a TypeScript declaration file.

A common approach is to keep all theme type extensions in one place:

src/
  types/
    theme.d.ts
  theme.ts

The declaration file is responsible for telling TypeScript about your custom theme tokens.

Your actual theme values should remain in your theme configuration.

For example:

import "rocksolidjs";
import { type ThemeColorVariants } from "rocksolidjs";

declare module "rocksolidjs" {
  interface ExtendedThemeColors {
    brand: ThemeColorVariants;
  }
  interface ExtendedThemeTypographyColors {
    link: string;
  }
}

Then define the values in your theme:

import createTheme from "rocksolidjs/createTheme";

export const theme = createTheme({
  colors: {
    brand: {
      // ...
    },
  },
  typography: {
    colors: {
      link: "text-blue-600 dark:text-blue-400",
    },
  },
});

The type extension only needs to be declared once.

You should not repeat the declare module block in every file where createTheme is used.

This keeps the responsibilities separate:

  • The declaration file defines the shape of custom tokens.
  • createTheme defines the values of those tokens.

Colors

Colors represent semantic color groups used throughout the component library.

A custom color can be added by extending ExtendedThemeColors.

For example, suppose your application has a brand color that does not exist in the default theme.

First, add the color to ExtendedThemeColors:

declare module "rocksolidjs" {
  interface ExtendedThemeColors {
    brand: ThemeColorVariants;
  }
}

The value must use ThemeColorVariants.

This means the custom color follows the same structure as the built-in color groups.

The value can then be added to createTheme:

createTheme({
  colors: {
    brand: {
      solid: {
        background: "bg-indigo-600 dark:bg-indigo-500",
        text: "text-white dark:text-white",
        border: "border-indigo-600 dark:border-indigo-500",
        hover: "hover:bg-indigo-700 dark:hover:bg-indigo-400",
        active: "active:bg-indigo-800 dark:active:bg-indigo-300",
      },
      filled: {
        background: "bg-indigo-100 dark:bg-indigo-950",
        text: "text-indigo-700 dark:text-indigo-300",
        border: "border-indigo-100 dark:border-indigo-950",
        hover: "hover:bg-indigo-200 dark:hover:bg-indigo-900",
        active: "active:bg-indigo-300 dark:active:bg-indigo-800",
      },
      outlined: {
        background: "bg-transparent dark:bg-transparent",
        text: "text-indigo-700 dark:text-indigo-300",
        border: "border-indigo-600 dark:border-indigo-500",
        hover: "hover:bg-indigo-50 dark:hover:bg-indigo-950",
        active: "active:bg-indigo-100 dark:active:bg-indigo-900",
      },
      ghost: {
        background: "bg-transparent dark:bg-transparent",
        text: "text-indigo-700 dark:text-indigo-300",
        border: "border-transparent dark:border-transparent",
        hover: "hover:bg-indigo-50 dark:hover:bg-indigo-950",
        active: "active:bg-indigo-100 dark:active:bg-indigo-900",
      },
    },
  },
});

Because brand is declared as ThemeColorVariants, TypeScript provides autocomplete and validates that the custom color follows the expected structure.

See Theme Tokens for more information about the color token structure.

Disabled

The disabled theme group contains tokens used when components are disabled.

To add custom disabled tokens, extend ExtendedThemeDisabled.

For example, if your application needs an opacity token:

declare module "rocksolidjs" {
  interface ExtendedThemeDisabled {
    opacity: string;
  }
}

You can then provide the value through createTheme:

createTheme({
  disabled: {
    opacity: "opacity-50",
  },
});

You can add as many custom disabled tokens as your application requires.

For example:

declare module "rocksolidjs" {
  interface ExtendedThemeDisabled {
    opacity: string;
    cursor: string;
  }
}

The corresponding theme values are:

createTheme({
  disabled: {
    opacity: "opacity-50",
    cursor: "cursor-not-allowed",
  },
});

Custom disabled tokens are simple string values, so they can contain the Tailwind CSS utilities required by your application.

Rounded

The rounded theme group defines the border-radius scale used by components.

To add a custom radius, extend ExtendedThemeRounded.

For example, add an extraLarge radius:

declare module "rocksolidjs" {
  interface ExtendedThemeRounded {
    extraLarge: string;
  }
}

Then provide the corresponding Tailwind CSS class:

createTheme({
  rounded: {
    extraLarge: "rounded-2xl",
  },
});

You can add multiple custom radius values:

declare module "rocksolidjs" {
  interface ExtendedThemeRounded {
    extraSmall: string;
    extraLarge: string;
  }
}

Then define their values:

createTheme({
  rounded: {
    extraSmall: "rounded-xs",
    extraLarge: "rounded-2xl",
  },
});

Custom rounded tokens can use any valid Tailwind CSS border-radius utility available in your project.

Shadow

The shadow theme group defines the elevation styles used by components.

To add a custom shadow, extend ExtendedThemeShadow.

For example:

declare module "rocksolidjs" {
  interface ExtendedThemeShadow {
    extraLarge: string;
  }
}

Then provide the corresponding value:

createTheme({
  shadow: {
    extraLarge: "shadow-2xl",
  },
});

Custom shadows can also use Tailwind CSS arbitrary values:

declare module "rocksolidjs" {
  interface ExtendedThemeShadow {
    elevated: string;
  }
}

Then define their values:

createTheme({
  shadow: {
    elevated: "shadow-[0_20px_40px_-10px_rgb(0_0_0_/_0.25)]",
  },
});

Typography

Typography can be extended in two areas:

  • Typography variants
  • Typography colors

These two areas have separate extension interfaces because they represent different types of tokens.

Typography Variants

Typography variants define reusable typography styles.

The default theme provides variants such as h1, h2, h3, body1, and body2.

If your application needs another typography style, extend ExtendedThemeTypographyVariants.

For example, add a caption variant:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyVariants {
    caption: string;
  }
}

Then provide the Tailwind CSS classes through createTheme:

createTheme({
  typography: {
    variants: {
      caption: "text-xs font-normal leading-4",
    },
  },
});

You can add multiple typography variants:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyVariants {
    caption: string;
    label: string;
    overline: string;
  }
}

Then define their values:

createTheme({
  typography: {
    variants: {
      caption: "text-xs font-normal leading-4",
      label: "text-sm font-medium leading-5",
      overline: "text-xs font-semibold uppercase tracking-wide",
    },
  },
});

Each typography variant is a string containing the Tailwind CSS classes that define the typography style.

Typography Colors

Typography colors define semantic colors for text.

The default theme provides colors such as textPrimary, textSecondary, and textTertiary.

If your application needs additional semantic text colors, extend ExtendedThemeTypographyColors.

For example, add a link color:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyColors {
    link: string;
  }
}

Then provide its value:

createTheme({
  typography: {
    colors: {
      link: "text-blue-600 dark:text-blue-400",
    },
  },
});

You can add multiple semantic typography colors:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyColors {
    link: string;
    caption: string;
  }
}

Then provide the corresponding Tailwind CSS classes:

createTheme({
  typography: {
    colors: {
      link: "text-blue-600 dark:text-blue-400",
      caption: "text-zinc-600 dark:text-zinc-400",
    },
  },
});

Extending Multiple Theme Groups

All theme extensions can be declared in the same TypeScript declaration file.

For example:

import "rocksolidjs";
import { type ThemeColorVariants } from "rocksolidjs";

declare module "rocksolidjs" {
  interface ExtendedThemeColors {
    brand: ThemeColorVariants;
  }
  interface ExtendedThemeDisabled {
    opacity: string;
  }
  interface ExtendedThemeRounded {
    extraLarge: string;
  }
  interface ExtendedThemeShadow {
    extraLarge: string;
  }
  interface ExtendedThemeTypographyVariants {
    caption: string;
  }
  interface ExtendedThemeTypographyColors {
    link: string;
  }
}

You should generally keep these declarations in one place rather than scattering them across multiple files.

The actual theme values can then be defined in your theme configuration:

createTheme({
  colors: {
    brand: {
      // ThemeColorVariants
    },
  },
  disabled: {
    opacity: "opacity-50",
  },
  rounded: {
    extraLarge: "rounded-2xl",
  },
  shadow: {
    extraLarge: "shadow-2xl",
  },
  typography: {
    variants: {
      caption: "text-xs font-normal leading-4",
    },
    colors: {
      link: "text-blue-600 dark:text-blue-400",
    },
  },
});

Why Extend the Types?

createTheme validates theme values against the theme schema.

When you add a custom token, TypeScript needs to know about that token before you can use it with type safety and autocomplete.

For example, after extending:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyColors {
    link: string;
  }
}

TypeScript recognizes link inside the theme configuration:

createTheme({
  typography: {
    colors: {
      link: "text-blue-600 dark:text-blue-400",
    },
  },
});

Without the extension, TypeScript does not know that link is a valid custom theme token.

For structured tokens such as colors, the extension also determines the structure that TypeScript expects.

For example:

declare module "rocksolidjs" {
  interface ExtendedThemeColors {
    brand: ThemeColorVariants;
  }
}

The brand token must therefore follow the ThemeColorVariants structure.

One-Time Type Extension

Type extensions should be declared once for your application.

For example, avoid doing this in multiple files:

declare module "rocksolidjs" {
  interface ExtendedThemeTypographyColors {
    link: string;
  }
}

Instead, keep your extensions in one declaration file:

src/
  types/
    theme.d.ts

Then use your custom tokens anywhere in your theme configuration without repeating the declaration.

This makes your custom theme API easier to maintain and prevents theme extensions from becoming scattered throughout the application.

Extension Points

Theme Group Extension Interface Value Type
Colors ExtendedThemeColors ThemeColorVariants
Disabled ExtendedThemeDisabled string
Rounded ExtendedThemeRounded string
Shadow ExtendedThemeShadow string
Typography Variants ExtendedThemeTypographyVariants string
Typography Colors ExtendedThemeTypographyColors string
  • Theme Tokens — Learn about available theme tokens and their expected structure.
  • createTheme — Create and customize a theme.