Skip to content

Custom Icon System

Navigation: HomeFeatures & Components → Custom Icons

Table of Contents

Introduction

The Custom Icon System provides SVG icon components for social media platforms and common actions. These custom icons complement Lucide React icons and maintain consistent styling across the application.

Available Icons

  • GitHubIcon: GitHub logo
  • LinkedInIcon: LinkedIn logo
  • FileDownloadIcon: Download/resume icon

Why Custom Icons?

  1. Brand Accuracy: Official logo SVGs match brand guidelines
  2. Performance: Inline SVGs load faster than images
  3. Styling Control: Can be styled with CSS/Tailwind
  4. Accessibility: Proper alt text and ARIA attributes
  5. Tree-Shaking: Only used icons are included in bundle

Icon Components

Export Index

```typescript:1:3:components/icons/index.ts export { GitHubIcon } from "./github-icon"; export { LinkedInIcon } from "./linkedin-icon"; export { FileDownloadIcon } from "./file-download-icon";

Centralized exports allow clean imports:

```typescript
import { GitHubIcon, LinkedInIcon } from "@/components/icons";

Implementation Details

GitHubIcon Component

```typescript:1:22:components/icons/github-icon.tsx import { SVGProps } from "react"; import { cn } from "@/lib/utils";

export function GitHubIcon({ className, ...props }: SVGProps) { return ( ); }

export default GitHubIcon;

### Icon Pattern

All custom icon components follow this pattern:

```typescript
import { SVGProps } from "react";
import { cn } from "@/lib/utils";

export function IconName({ className, ...props }: SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="..." // Original viewBox
      className={cn("size-full", className)}  // Default + custom classes
      {...props}  // Spread remaining SVG props
    >
      {/* SVG path data */}
    </svg>
  );
}

export default IconName;

Key Features

  1. TypeScript Props: Accepts all standard SVG props
  2. className Merging: Uses cn() to merge classes
  3. Default Sizing: size-full makes icon fill parent
  4. currentColor: fill="currentColor" inherits text color
  5. Accessible: Can add aria-label and role via props

Usage Examples

Basic Icon

import { GitHubIcon } from "@/components/icons";

<GitHubIcon className="h-6 w-6" />

With Custom Color

<GitHubIcon className="h-8 w-8 text-blue-500" />

The icon uses fill="currentColor", so text-* classes control the color.

import { GitHubIcon } from "@/components/icons";
import SocialLink from "@/components/social-link";

<SocialLink
  href="https://github.com/username"
  ariaLabel="GitHub Profile"
  icon={GitHubIcon}
/>

Hover Effects

<GitHubIcon className="h-6 w-6 text-foreground hover:text-primary transition-colors" />

Responsive Sizing

<GitHubIcon className="h-5 w-5 md:h-6 md:w-6" />

With ARIA Label

<GitHubIcon
  className="h-6 w-6"
  aria-label="GitHub"
  role="img"
/>

Inline with Text

<a href="https://github.com/username" className="flex items-center gap-2">
  <GitHubIcon className="h-5 w-5" />
  <span>View on GitHub</span>
</a>

Creating Custom Icons

Step 1: Obtain SVG

Get official brand SVG from:

Step 2: Create Component File

// components/icons/twitter-icon.tsx
import { SVGProps } from "react";
import { cn } from "@/lib/utils";

export function TwitterIcon({ className, ...props }: SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      className={cn("size-full", className)}
      {...props}
    >
      <path
        fill="currentColor"
        d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"
      />
    </svg>
  );
}

export default TwitterIcon;

Step 3: Update Index

// components/icons/index.ts
export { GitHubIcon } from "./github-icon";
export { LinkedInIcon } from "./linkedin-icon";
export { FileDownloadIcon } from "./file-download-icon";
export { TwitterIcon } from "./twitter-icon"; // Add new icon

Step 4: Use Icon

import { TwitterIcon } from "@/components/icons";

<TwitterIcon className="h-6 w-6" />

Best Practices

Sizing

// ✅ Good - Use Tailwind size classes
<GitHubIcon className="h-6 w-6" />
<GitHubIcon className="size-8" />

// ❌ Avoid - Inline styles
<GitHubIcon style={{ width: '24px', height: '24px' }} />

Coloring

// ✅ Good - Use text color (works with currentColor)
<GitHubIcon className="text-primary" />
<GitHubIcon className="text-blue-500 hover:text-blue-600" />

// ❌ Avoid - fill attribute (overrides currentColor)
<GitHubIcon fill="#3b82f6" />

Accessibility

// ✅ Good - Decorative icon (in link with text)
<a href="...">
  <GitHubIcon className="h-5 w-5" aria-hidden="true" />
  <span>GitHub</span>
</a>

// ✅ Good - Standalone icon (needs label)
<button>
  <GitHubIcon className="h-5 w-5" aria-label="GitHub" role="img" />
</button>

// ❌ Avoid - No context
<GitHubIcon className="h-5 w-5" />

Performance

// ✅ Good - Import only needed icons
import { GitHubIcon, LinkedInIcon } from "@/components/icons";

// ❌ Avoid - Import entire index (larger bundle)
import * as Icons from "@/components/icons";

Naming

// ✅ Good - Descriptive, suffixed with "Icon"
GitHubIcon;
LinkedInIcon;
TwitterIcon;

// ❌ Avoid - Vague names
GitHub;
Logo;
Social;

See Also

Next Steps

  1. Icon Library Expansion: Add more social platform icons
  2. Animated Icons: Add hover animations with CSS/GSAP
  3. Icon Sprite Sheet: Combine into SVG sprite for better caching
  4. Icon Generator: Script to automate icon component creation
  5. Duotone Icons: Support two-color icons
  6. Icon Documentation: Create visual icon catalog page

Last Updated: 2024-02-09
Related Docs: Components | Social Utilities | Footer