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:
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.
import defaultColors from "tailwindcss/colors";
export type DefaultColors = keyof typeof defaultColors;
const color: DefaultColors = 'blue'
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.
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:
.png&w=1920&q=75)
Let's combine the above information to create a new type called ColorShade
type ColorShade<T extends DefaultColors> = keyof ColorConfig[T] extends
  | string
  | number
  ? keyof ColorConfig[T]
  : never;
const shade: ColorShade<'blue'> = '500'.png&w=1920&q=75)
Now we can also get the properties after the color.
By combining these, we can extract names that combine color and brightness.
type TailwindColorClass = {
  [P in DefaultColors]: ColorShade<P> extends never ? P : `${P}-${ColorShade<P>}`;
}[DefaultColors];.png&w=1920&q=75)
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.
interface IconProps {
  fill: `fill-${TailwindColorClass}`;
  stroke: `storke-${TailwindColorClass}`;
}.png&w=1920&q=75)
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.
const color = {
  ...formalColors,
  ...keyColors,
  ...accentColors,
  ...grayColor,
  ...theme.colors,
}And instead of using DefaultColors used above, let's create the following type
type CustomColors = typeof color;
type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors;Final Code
tailwin.config.ts
type CustomColors = typeof color;
export type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors;Component.tsx
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}`;
}
 Please let me know if there are any mistranslations!
Please let me know if there are any mistranslations!