Extracting Color Properties from Tailwind CSS

profile image

Let\'s extract color types set in Tailwind CSS!

This post has been translated by Jetbrains's Coding Agent Junie junie logoPlease let me know if there are any mistranslations!

When creating an icon component, I wanted to receive color-related properties as props and add them to className.

Icons are composed of SVG, and instead of directly specifying fill and stroke values, I wanted to use Tailwind CSS's autocompletion to specify colors in className.

For example, I wanted to write code like this:

typescript
interface IconProps {
  fill: string;
  stroke: string;
}
const Icon = ({fill, stroke}: IconProps) => {
  <svg className={`${fill} ${stroke} `} />
}

To leverage Tailwind's autocompletion, I needed to use a type other than string for the fill and stroke types. In other words, I needed to extract the names of color classes configured in Tailwind.

Extracting Types

First, we can get the basic color information from Tailwind.

typescript
import defaultColors from "tailwindcss/colors";
export type DefaultColors = keyof typeof defaultColors;

const color: DefaultColors = 'blue'

Image.png

However, only the color keys are autocompleted, not the properties that follow. (blue works, but blue-500 etc. are not autocompleted)

Next, let's get the entire Tailwind config.

typescript
import resolveConfig from "tailwindcss/resolveConfig";
import tailwindConfig from "web/tailwind.config.ts"; // Tailwind config file

const fullConfig = resolveConfig(tailwindConfig);

Then we can get the following information from fullConfig:

Image.png

Let's combine the above information to create a new type called ColorShade

typescript
type ColorShade<T extends DefaultColors> = keyof ColorConfig[T] extends
  | string
  | number
  ? keyof ColorConfig[T]
  : never;

const shade: ColorShade<'blue'> = '500'

Image.png

Now we can also get the properties after the color.

By combining these, we can extract names that combine color and brightness.

typescript
type TailwindColorClass = {
  [P in DefaultColors]: ColorShade<P> extends never ? P : `${P}-${ColorShade<P>}`;
}[DefaultColors];

Image.png

Application

Now let's create className that can go into fill for fill and className that can only go into stroke for stroke, which was the functionality I wanted.

typescript
interface IconProps {
  fill: `fill-${TailwindColorClass}`;
  stroke: `storke-${TailwindColorClass}`;
}

Image.png

Applying Custom Colors

The problem with the above code is that it only gets Tailwind's default colors and cannot get user-defined colors.

Separating Custom Colors

First, let's separate the custom-applied colors into a single variable.

typescript
const color = {
  ...formalColors,
  ...keyColors,
  ...accentColors,
  ...grayColor,
  ...theme.colors,
}

And instead of using DefaultColors used above, let's create the following type

typescript
type CustomColors = typeof color;
type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors;

Final Code

tailwin.config.ts

typescript
type CustomColors = typeof color;
export type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors;

Component.tsx

typescript
const fullConfig = resolveConfig(tailwindConfig);
const colorTheme = fullConfig.theme.colors as Colors;
type ColorConfig = typeof colorTheme;
type ColorKeys = keyof Colors;

type ColorShade<T extends ColorKeys> = keyof ColorConfig[T] extends
  | string
  | number
  ? keyof ColorConfig[T]
  : never;


type TailwindColorClass = {
  [P in ColorKeys]: ColorShade<P> extends never ? P : `${P}-${ColorShade<P>}`;
}[ColorKeys];



interface IconProps {
  fill: `fill-${TailwindColorClass}`;
  stroke: `storke-${TailwindColorClass}`;
}
❤️ 0
🔥 0
😎 0
⭐️ 0
🆒 0