Skip to content

Instantly share code, notes, and snippets.

@YuraSab
Created September 25, 2026 10:39
Show Gist options
  • Select an option

  • Save YuraSab/033444cc231a23c7d6d4a126fea32ef2 to your computer and use it in GitHub Desktop.

Select an option

Save YuraSab/033444cc231a23c7d6d4a126fea32ef2 to your computer and use it in GitHub Desktop.
// src/components/ui/Icon/Icon.tsx
import * as LucideIcons from "lucide-react";
import { tv, type VariantProps } from "tailwind-variants";
export const iconVariants = tv({
base: "shrink-0 transition-colors",
variants: {
size: {
xs: "size-3.5", // 14px
sm: "size-4", // 16px
md: "size-5", // 20px
lg: "size-6", // 24px
xl: "size-8", // 32px
},
color: {
primary: "text-blue-600",
error: "text-red-600",
success: "text-green-600",
muted: "text-gray-400",
current: "text-current",
},
},
defaultVariants: {
size: "sm",
color: "current",
},
});
export type IconName = keyof typeof LucideIcons;
export interface IconProps extends VariantProps<typeof iconVariants> {
name: IconName;
className?: string;
strokeWidth?: number;
customSize?: number;
}
export const Icon = ({
name,
size,
color,
className,
strokeWidth = 2,
customSize,
}: IconProps) => {
// Lucide types their exports as LucideIcon
const IconComponent = LucideIcons[name] as LucideIcons.LucideIcon;
// динамічний клас для кастомного розміру
const customSizeClass = customSize ? `size-[${customSize}px]` : undefined;
// об'єднюю класи, фільтруючи потенційний undefined
const combinedClasses = [className, customSizeClass]
.filter(Boolean)
.join(" ");
return (
<IconComponent
// передаю customSize, щоб синхронізувати width/height із значенням в класі (не обов'язково, оскільки пріоритет в класу, проте є хорошою практикою)
size={customSize}
className={iconVariants({
// виключаю дефолтний розмір, якщо передано customSize
size: customSize ? undefined : size,
color,
className: combinedClasses,
})}
strokeWidth={strokeWidth}
/>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment